Allow specifying an access method for partitioned tables
[pgsql.git] / src / backend / commands / tablecmds.c
blob259b4237a2430cc167429b6b484935da3fd9aca7
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/gist.h"
20 #include "access/heapam.h"
21 #include "access/heapam_xlog.h"
22 #include "access/multixact.h"
23 #include "access/reloptions.h"
24 #include "access/relscan.h"
25 #include "access/sysattr.h"
26 #include "access/tableam.h"
27 #include "access/toast_compression.h"
28 #include "access/xact.h"
29 #include "access/xlog.h"
30 #include "access/xloginsert.h"
31 #include "catalog/catalog.h"
32 #include "catalog/heap.h"
33 #include "catalog/index.h"
34 #include "catalog/namespace.h"
35 #include "catalog/objectaccess.h"
36 #include "catalog/partition.h"
37 #include "catalog/pg_am.h"
38 #include "catalog/pg_attrdef.h"
39 #include "catalog/pg_collation.h"
40 #include "catalog/pg_constraint.h"
41 #include "catalog/pg_depend.h"
42 #include "catalog/pg_foreign_table.h"
43 #include "catalog/pg_inherits.h"
44 #include "catalog/pg_largeobject.h"
45 #include "catalog/pg_namespace.h"
46 #include "catalog/pg_opclass.h"
47 #include "catalog/pg_statistic_ext.h"
48 #include "catalog/pg_tablespace.h"
49 #include "catalog/pg_trigger.h"
50 #include "catalog/pg_type.h"
51 #include "catalog/storage.h"
52 #include "catalog/storage_xlog.h"
53 #include "catalog/toasting.h"
54 #include "commands/cluster.h"
55 #include "commands/comment.h"
56 #include "commands/defrem.h"
57 #include "commands/event_trigger.h"
58 #include "commands/sequence.h"
59 #include "commands/tablecmds.h"
60 #include "commands/tablespace.h"
61 #include "commands/trigger.h"
62 #include "commands/typecmds.h"
63 #include "commands/user.h"
64 #include "commands/vacuum.h"
65 #include "executor/executor.h"
66 #include "foreign/fdwapi.h"
67 #include "foreign/foreign.h"
68 #include "miscadmin.h"
69 #include "nodes/makefuncs.h"
70 #include "nodes/nodeFuncs.h"
71 #include "nodes/parsenodes.h"
72 #include "optimizer/optimizer.h"
73 #include "parser/parse_coerce.h"
74 #include "parser/parse_collate.h"
75 #include "parser/parse_expr.h"
76 #include "parser/parse_relation.h"
77 #include "parser/parse_type.h"
78 #include "parser/parse_utilcmd.h"
79 #include "parser/parser.h"
80 #include "partitioning/partbounds.h"
81 #include "partitioning/partdesc.h"
82 #include "pgstat.h"
83 #include "rewrite/rewriteDefine.h"
84 #include "rewrite/rewriteHandler.h"
85 #include "rewrite/rewriteManip.h"
86 #include "storage/bufmgr.h"
87 #include "storage/lmgr.h"
88 #include "storage/lock.h"
89 #include "storage/predicate.h"
90 #include "storage/smgr.h"
91 #include "tcop/utility.h"
92 #include "utils/acl.h"
93 #include "utils/builtins.h"
94 #include "utils/fmgroids.h"
95 #include "utils/inval.h"
96 #include "utils/lsyscache.h"
97 #include "utils/memutils.h"
98 #include "utils/partcache.h"
99 #include "utils/relcache.h"
100 #include "utils/ruleutils.h"
101 #include "utils/snapmgr.h"
102 #include "utils/syscache.h"
103 #include "utils/timestamp.h"
104 #include "utils/typcache.h"
105 #include "utils/usercontext.h"
108 * ON COMMIT action list
110 typedef struct OnCommitItem
112 Oid relid; /* relid of relation */
113 OnCommitAction oncommit; /* what to do at end of xact */
116 * If this entry was created during the current transaction,
117 * creating_subid is the ID of the creating subxact; if created in a prior
118 * transaction, creating_subid is zero. If deleted during the current
119 * transaction, deleting_subid is the ID of the deleting subxact; if no
120 * deletion request is pending, deleting_subid is zero.
122 SubTransactionId creating_subid;
123 SubTransactionId deleting_subid;
124 } OnCommitItem;
126 static List *on_commits = NIL;
130 * State information for ALTER TABLE
132 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
133 * structs, one for each table modified by the operation (the named table
134 * plus any child tables that are affected). We save lists of subcommands
135 * to apply to this table (possibly modified by parse transformation steps);
136 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
137 * necessary information is stored in the constraints and newvals lists.
139 * Phase 2 is divided into multiple passes; subcommands are executed in
140 * a pass determined by subcommand type.
143 typedef enum AlterTablePass
145 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
146 AT_PASS_DROP, /* DROP (all flavors) */
147 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
148 AT_PASS_ADD_COL, /* ADD COLUMN */
149 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
150 AT_PASS_OLD_INDEX, /* re-add existing indexes */
151 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
152 /* We could support a RENAME COLUMN pass here, but not currently used */
153 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
154 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
155 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
156 AT_PASS_ADD_INDEX, /* ADD indexes */
157 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
158 AT_PASS_MISC, /* other stuff */
159 } AlterTablePass;
161 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
163 typedef struct AlteredTableInfo
165 /* Information saved before any work commences: */
166 Oid relid; /* Relation to work on */
167 char relkind; /* Its relkind */
168 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
171 * Transiently set during Phase 2, normally set to NULL.
173 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
174 * returns control. This can be exploited by ATExecCmd subroutines to
175 * close/reopen across transaction boundaries.
177 Relation rel;
179 /* Information saved by Phase 1 for Phase 2: */
180 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
181 /* Information saved by Phases 1/2 for Phase 3: */
182 List *constraints; /* List of NewConstraint */
183 List *newvals; /* List of NewColumnValue */
184 List *afterStmts; /* List of utility command parsetrees */
185 bool verify_new_notnull; /* T if we should recheck NOT NULL */
186 int rewrite; /* Reason for forced rewrite, if any */
187 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
188 Oid newAccessMethod; /* new access method; 0 means no change,
189 * if above is true */
190 Oid newTableSpace; /* new tablespace; 0 means no change */
191 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
192 char newrelpersistence; /* if above is true */
193 Expr *partition_constraint; /* for attach partition validation */
194 /* true, if validating default due to some other attach/detach */
195 bool validate_default;
196 /* Objects to rebuild after completing ALTER TYPE operations */
197 List *changedConstraintOids; /* OIDs of constraints to rebuild */
198 List *changedConstraintDefs; /* string definitions of same */
199 List *changedIndexOids; /* OIDs of indexes to rebuild */
200 List *changedIndexDefs; /* string definitions of same */
201 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
202 char *clusterOnIndex; /* index to use for CLUSTER */
203 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
204 List *changedStatisticsDefs; /* string definitions of same */
205 } AlteredTableInfo;
207 /* Struct describing one new constraint to check in Phase 3 scan */
208 /* Note: new not-null constraints are handled elsewhere */
209 typedef struct NewConstraint
211 char *name; /* Constraint name, or NULL if none */
212 ConstrType contype; /* CHECK or FOREIGN */
213 Oid refrelid; /* PK rel, if FOREIGN */
214 Oid refindid; /* OID of PK's index, if FOREIGN */
215 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
216 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
217 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
218 ExprState *qualstate; /* Execution state for CHECK expr */
219 } NewConstraint;
222 * Struct describing one new column value that needs to be computed during
223 * Phase 3 copy (this could be either a new column with a non-null default, or
224 * a column that we're changing the type of). Columns without such an entry
225 * are just copied from the old table during ATRewriteTable. Note that the
226 * expr is an expression over *old* table values, except when is_generated
227 * is true; then it is an expression over columns of the *new* tuple.
229 typedef struct NewColumnValue
231 AttrNumber attnum; /* which column */
232 Expr *expr; /* expression to compute */
233 ExprState *exprstate; /* execution state */
234 bool is_generated; /* is it a GENERATED expression? */
235 } NewColumnValue;
238 * Error-reporting support for RemoveRelations
240 struct dropmsgstrings
242 char kind;
243 int nonexistent_code;
244 const char *nonexistent_msg;
245 const char *skipping_msg;
246 const char *nota_msg;
247 const char *drophint_msg;
250 static const struct dropmsgstrings dropmsgstringarray[] = {
251 {RELKIND_RELATION,
252 ERRCODE_UNDEFINED_TABLE,
253 gettext_noop("table \"%s\" does not exist"),
254 gettext_noop("table \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a table"),
256 gettext_noop("Use DROP TABLE to remove a table.")},
257 {RELKIND_SEQUENCE,
258 ERRCODE_UNDEFINED_TABLE,
259 gettext_noop("sequence \"%s\" does not exist"),
260 gettext_noop("sequence \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a sequence"),
262 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
263 {RELKIND_VIEW,
264 ERRCODE_UNDEFINED_TABLE,
265 gettext_noop("view \"%s\" does not exist"),
266 gettext_noop("view \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a view"),
268 gettext_noop("Use DROP VIEW to remove a view.")},
269 {RELKIND_MATVIEW,
270 ERRCODE_UNDEFINED_TABLE,
271 gettext_noop("materialized view \"%s\" does not exist"),
272 gettext_noop("materialized view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a materialized view"),
274 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
275 {RELKIND_INDEX,
276 ERRCODE_UNDEFINED_OBJECT,
277 gettext_noop("index \"%s\" does not exist"),
278 gettext_noop("index \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not an index"),
280 gettext_noop("Use DROP INDEX to remove an index.")},
281 {RELKIND_COMPOSITE_TYPE,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("type \"%s\" does not exist"),
284 gettext_noop("type \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a type"),
286 gettext_noop("Use DROP TYPE to remove a type.")},
287 {RELKIND_FOREIGN_TABLE,
288 ERRCODE_UNDEFINED_OBJECT,
289 gettext_noop("foreign table \"%s\" does not exist"),
290 gettext_noop("foreign table \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a foreign table"),
292 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
293 {RELKIND_PARTITIONED_TABLE,
294 ERRCODE_UNDEFINED_TABLE,
295 gettext_noop("table \"%s\" does not exist"),
296 gettext_noop("table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a table"),
298 gettext_noop("Use DROP TABLE to remove a table.")},
299 {RELKIND_PARTITIONED_INDEX,
300 ERRCODE_UNDEFINED_OBJECT,
301 gettext_noop("index \"%s\" does not exist"),
302 gettext_noop("index \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not an index"),
304 gettext_noop("Use DROP INDEX to remove an index.")},
305 {'\0', 0, NULL, NULL, NULL, NULL}
308 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
309 struct DropRelationCallbackState
311 /* These fields are set by RemoveRelations: */
312 char expected_relkind;
313 LOCKMODE heap_lockmode;
314 /* These fields are state to track which subsidiary locks are held: */
315 Oid heapOid;
316 Oid partParentOid;
317 /* These fields are passed back by RangeVarCallbackForDropRelation: */
318 char actual_relkind;
319 char actual_relpersistence;
322 /* Alter table target-type flags for ATSimplePermissions */
323 #define ATT_TABLE 0x0001
324 #define ATT_VIEW 0x0002
325 #define ATT_MATVIEW 0x0004
326 #define ATT_INDEX 0x0008
327 #define ATT_COMPOSITE_TYPE 0x0010
328 #define ATT_FOREIGN_TABLE 0x0020
329 #define ATT_PARTITIONED_INDEX 0x0040
330 #define ATT_SEQUENCE 0x0080
333 * ForeignTruncateInfo
335 * Information related to truncation of foreign tables. This is used for
336 * the elements in a hash table. It uses the server OID as lookup key,
337 * and includes a per-server list of all foreign tables involved in the
338 * truncation.
340 typedef struct ForeignTruncateInfo
342 Oid serverid;
343 List *rels;
344 } ForeignTruncateInfo;
347 * Partition tables are expected to be dropped when the parent partitioned
348 * table gets dropped. Hence for partitioning we use AUTO dependency.
349 * Otherwise, for regular inheritance use NORMAL dependency.
351 #define child_dependency_type(child_is_partition) \
352 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
354 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
355 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
356 static void truncate_check_activity(Relation rel);
357 static void RangeVarCallbackForTruncate(const RangeVar *relation,
358 Oid relId, Oid oldRelId, void *arg);
359 static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
360 bool is_partition, List **supconstr,
361 List **supnotnulls);
362 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
363 static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
364 static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
365 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
366 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
367 static void StoreCatalogInheritance(Oid relationId, List *supers,
368 bool child_is_partition);
369 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
370 int32 seqNumber, Relation inhRelation,
371 bool child_is_partition);
372 static int findAttrByName(const char *attributeName, const List *columns);
373 static void AlterIndexNamespaces(Relation classRel, Relation rel,
374 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
375 static void AlterSeqNamespaces(Relation classRel, Relation rel,
376 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
377 LOCKMODE lockmode);
378 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
379 bool recurse, bool recursing, LOCKMODE lockmode);
380 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
381 Relation rel, HeapTuple contuple, List **otherrelids,
382 LOCKMODE lockmode);
383 static ObjectAddress ATExecValidateConstraint(List **wqueue,
384 Relation rel, char *constrName,
385 bool recurse, bool recursing, LOCKMODE lockmode);
386 static int transformColumnNameList(Oid relId, List *colList,
387 int16 *attnums, Oid *atttypids);
388 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
389 List **attnamelist,
390 int16 *attnums, Oid *atttypids,
391 Oid *opclasses, bool *pk_has_without_overlaps);
392 static Oid transformFkeyCheckAttrs(Relation pkrel,
393 int numattrs, int16 *attnums,
394 bool with_period, Oid *opclasses,
395 bool *pk_has_without_overlaps);
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, bool hasperiod);
402 static void ATController(AlterTableStmt *parsetree,
403 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
404 AlterTableUtilityContext *context);
405 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
406 bool recurse, bool recursing, LOCKMODE lockmode,
407 AlterTableUtilityContext *context);
408 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
409 AlterTableUtilityContext *context);
410 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
411 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
412 AlterTableUtilityContext *context);
413 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
414 Relation rel, AlterTableCmd *cmd,
415 bool recurse, LOCKMODE lockmode,
416 AlterTablePass cur_pass,
417 AlterTableUtilityContext *context);
418 static void ATRewriteTables(AlterTableStmt *parsetree,
419 List **wqueue, LOCKMODE lockmode,
420 AlterTableUtilityContext *context);
421 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
422 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
423 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
424 static void ATSimpleRecursion(List **wqueue, Relation rel,
425 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
426 AlterTableUtilityContext *context);
427 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
428 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
429 LOCKMODE lockmode,
430 AlterTableUtilityContext *context);
431 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
432 DropBehavior behavior);
433 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
434 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
435 AlterTableUtilityContext *context);
436 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
437 Relation rel, AlterTableCmd **cmd,
438 bool recurse, bool recursing,
439 LOCKMODE lockmode, AlterTablePass cur_pass,
440 AlterTableUtilityContext *context);
441 static bool check_for_column_name_collision(Relation rel, const char *colname,
442 bool if_not_exists);
443 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
444 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
445 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
446 LOCKMODE lockmode);
447 static bool set_attnotnull(List **wqueue, Relation rel,
448 AttrNumber attnum, bool recurse, LOCKMODE lockmode);
449 static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
450 char *constrname, char *colName,
451 bool recurse, bool recursing,
452 List **readyRels, LOCKMODE lockmode);
453 static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
454 const char *colName, LOCKMODE lockmode);
455 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
456 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
457 List *testConstraint, List *provenConstraint);
458 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
459 Node *newDefault, LOCKMODE lockmode);
460 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
461 Node *newDefault);
462 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
463 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
464 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
465 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
466 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
467 bool recurse, bool recursing);
468 static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
469 Node *newExpr, LOCKMODE lockmode);
470 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
471 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
472 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
473 Node *newValue, LOCKMODE lockmode);
474 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
475 Node *options, bool isReset, LOCKMODE lockmode);
476 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
477 Node *newValue, LOCKMODE lockmode);
478 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
479 AlterTableCmd *cmd, LOCKMODE lockmode,
480 AlterTableUtilityContext *context);
481 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
482 DropBehavior behavior,
483 bool recurse, bool recursing,
484 bool missing_ok, LOCKMODE lockmode,
485 ObjectAddresses *addrs);
486 static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
487 LOCKMODE lockmode, AlterTableUtilityContext *context);
488 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
489 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
490 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
491 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
492 static ObjectAddress ATExecAddConstraint(List **wqueue,
493 AlteredTableInfo *tab, Relation rel,
494 Constraint *newConstraint, bool recurse, bool is_readd,
495 LOCKMODE lockmode);
496 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
497 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
498 IndexStmt *stmt, LOCKMODE lockmode);
499 static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
500 AlteredTableInfo *tab, Relation rel,
501 Constraint *constr,
502 bool recurse, bool recursing, bool is_readd,
503 LOCKMODE lockmode);
504 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
505 Relation rel, Constraint *fkconstraint,
506 bool recurse, bool recursing,
507 LOCKMODE lockmode);
508 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
509 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
510 int numfks, int16 *pkattnum, int16 *fkattnum,
511 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
512 int numfkdelsetcols, int16 *fkdelsetcols,
513 bool old_check_ok,
514 Oid parentDelTrigger, Oid parentUpdTrigger,
515 bool with_period);
516 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
517 int numfksetcols, const int16 *fksetcolsattnums,
518 List *fksetcols);
519 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
520 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
521 int numfks, int16 *pkattnum, int16 *fkattnum,
522 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
523 int numfkdelsetcols, int16 *fkdelsetcols,
524 bool old_check_ok, LOCKMODE lockmode,
525 Oid parentInsTrigger, Oid parentUpdTrigger,
526 bool with_period);
528 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
529 Relation partitionRel);
530 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
531 static void CloneFkReferencing(List **wqueue, Relation parentRel,
532 Relation partRel);
533 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
534 Constraint *fkconstraint, Oid constraintOid,
535 Oid indexOid,
536 Oid parentInsTrigger, Oid parentUpdTrigger,
537 Oid *insertTrigOid, Oid *updateTrigOid);
538 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
539 Constraint *fkconstraint, Oid constraintOid,
540 Oid indexOid,
541 Oid parentDelTrigger, Oid parentUpdTrigger,
542 Oid *deleteTrigOid, Oid *updateTrigOid);
543 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
544 Oid partRelid,
545 Oid parentConstrOid, int numfks,
546 AttrNumber *mapped_conkey, AttrNumber *confkey,
547 Oid *conpfeqop,
548 Oid parentInsTrigger,
549 Oid parentUpdTrigger,
550 Relation trigrel);
551 static void GetForeignKeyActionTriggers(Relation trigrel,
552 Oid conoid, Oid confrelid, Oid conrelid,
553 Oid *deleteTriggerOid,
554 Oid *updateTriggerOid);
555 static void GetForeignKeyCheckTriggers(Relation trigrel,
556 Oid conoid, Oid confrelid, Oid conrelid,
557 Oid *insertTriggerOid,
558 Oid *updateTriggerOid);
559 static void ATExecDropConstraint(Relation rel, const char *constrName,
560 DropBehavior behavior, bool recurse,
561 bool missing_ok, LOCKMODE lockmode);
562 static ObjectAddress dropconstraint_internal(Relation rel,
563 HeapTuple constraintTup, DropBehavior behavior,
564 bool recurse, bool recursing,
565 bool missing_ok, List **readyRels,
566 LOCKMODE lockmode);
567 static void ATPrepAlterColumnType(List **wqueue,
568 AlteredTableInfo *tab, Relation rel,
569 bool recurse, bool recursing,
570 AlterTableCmd *cmd, LOCKMODE lockmode,
571 AlterTableUtilityContext *context);
572 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
573 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
574 AlterTableCmd *cmd, LOCKMODE lockmode);
575 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
576 Relation rel, AttrNumber attnum, const char *colName);
577 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
578 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
579 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
580 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
581 LOCKMODE lockmode);
582 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
583 char *cmd, List **wqueue, LOCKMODE lockmode,
584 bool rewrite);
585 static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
586 Oid objid, Relation rel, List *domname,
587 const char *conname);
588 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
589 static void TryReuseForeignKey(Oid oldId, Constraint *con);
590 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
591 List *options, LOCKMODE lockmode);
592 static void change_owner_fix_column_acls(Oid relationOid,
593 Oid oldOwnerId, Oid newOwnerId);
594 static void change_owner_recurse_to_sequences(Oid relationOid,
595 Oid newOwnerId, LOCKMODE lockmode);
596 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
597 LOCKMODE lockmode);
598 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
599 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
600 static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
601 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
602 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
603 const char *tablespacename, LOCKMODE lockmode);
604 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
605 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
606 static void ATExecSetRelOptions(Relation rel, List *defList,
607 AlterTableType operation,
608 LOCKMODE lockmode);
609 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
610 char fires_when, bool skip_system, bool recurse,
611 LOCKMODE lockmode);
612 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
613 char fires_when, LOCKMODE lockmode);
614 static void ATPrepAddInherit(Relation child_rel);
615 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
616 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
617 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
618 DependencyType deptype);
619 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
620 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
621 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
622 static void ATExecGenericOptions(Relation rel, List *options);
623 static void ATExecSetRowSecurity(Relation rel, bool rls);
624 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
625 static ObjectAddress ATExecSetCompression(Relation rel,
626 const char *column, Node *newValue, LOCKMODE lockmode);
628 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
629 static const char *storage_name(char c);
631 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
632 Oid oldRelOid, void *arg);
633 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
634 Oid oldrelid, void *arg);
635 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
636 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
637 List **partexprs, Oid *partopclass, Oid *partcollation,
638 PartitionStrategy strategy);
639 static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
640 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
641 bool expect_detached);
642 static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
643 int inhcount);
644 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
645 PartitionCmd *cmd,
646 AlterTableUtilityContext *context);
647 static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
648 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
649 List *partConstraint,
650 bool validate_default);
651 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
652 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
653 static void DropClonedTriggersFromPartition(Oid partitionId);
654 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
655 Relation rel, RangeVar *name,
656 bool concurrent);
657 static void DetachPartitionFinalize(Relation rel, Relation partRel,
658 bool concurrent, Oid defaultPartOid);
659 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
660 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
661 RangeVar *name);
662 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
663 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
664 Relation partitionTbl);
665 static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
666 static List *GetParentedForeignKeyRefs(Relation partition);
667 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
668 static char GetAttributeCompression(Oid atttypid, const char *compression);
669 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
672 /* ----------------------------------------------------------------
673 * DefineRelation
674 * Creates a new relation.
676 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
677 * The other arguments are used to extend the behavior for other cases:
678 * relkind: relkind to assign to the new relation
679 * ownerId: if not InvalidOid, use this as the new relation's owner.
680 * typaddress: if not null, it's set to the pg_type entry's address.
681 * queryString: for error reporting
683 * Note that permissions checks are done against current user regardless of
684 * ownerId. A nonzero ownerId is used when someone is creating a relation
685 * "on behalf of" someone else, so we still want to see that the current user
686 * has permissions to do it.
688 * If successful, returns the address of the new relation.
689 * ----------------------------------------------------------------
691 ObjectAddress
692 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
693 ObjectAddress *typaddress, const char *queryString)
695 char relname[NAMEDATALEN];
696 Oid namespaceId;
697 Oid relationId;
698 Oid tablespaceId;
699 Relation rel;
700 TupleDesc descriptor;
701 List *inheritOids;
702 List *old_constraints;
703 List *old_notnulls;
704 List *rawDefaults;
705 List *cookedDefaults;
706 List *nncols;
707 Datum reloptions;
708 ListCell *listptr;
709 AttrNumber attnum;
710 bool partitioned;
711 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
712 Oid ofTypeId;
713 ObjectAddress address;
714 LOCKMODE parentLockmode;
715 Oid accessMethodId = InvalidOid;
718 * Truncate relname to appropriate length (probably a waste of time, as
719 * parser should have done this already).
721 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
724 * Check consistency of arguments
726 if (stmt->oncommit != ONCOMMIT_NOOP
727 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
728 ereport(ERROR,
729 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
730 errmsg("ON COMMIT can only be used on temporary tables")));
732 if (stmt->partspec != NULL)
734 if (relkind != RELKIND_RELATION)
735 elog(ERROR, "unexpected relkind: %d", (int) relkind);
737 relkind = RELKIND_PARTITIONED_TABLE;
738 partitioned = true;
740 else
741 partitioned = false;
744 * Look up the namespace in which we are supposed to create the relation,
745 * check we have permission to create there, lock it against concurrent
746 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
747 * namespace is selected.
749 namespaceId =
750 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
753 * Security check: disallow creating temp tables from security-restricted
754 * code. This is needed because calling code might not expect untrusted
755 * tables to appear in pg_temp at the front of its search path.
757 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
758 && InSecurityRestrictedOperation())
759 ereport(ERROR,
760 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
761 errmsg("cannot create temporary table within security-restricted operation")));
764 * Determine the lockmode to use when scanning parents. A self-exclusive
765 * lock is needed here.
767 * For regular inheritance, if two backends attempt to add children to the
768 * same parent simultaneously, and that parent has no pre-existing
769 * children, then both will attempt to update the parent's relhassubclass
770 * field, leading to a "tuple concurrently updated" error. Also, this
771 * interlocks against a concurrent ANALYZE on the parent table, which
772 * might otherwise be attempting to clear the parent's relhassubclass
773 * field, if its previous children were recently dropped.
775 * If the child table is a partition, then we instead grab an exclusive
776 * lock on the parent because its partition descriptor will be changed by
777 * addition of the new partition.
779 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
780 ShareUpdateExclusiveLock);
782 /* Determine the list of OIDs of the parents. */
783 inheritOids = NIL;
784 foreach(listptr, stmt->inhRelations)
786 RangeVar *rv = (RangeVar *) lfirst(listptr);
787 Oid parentOid;
789 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
792 * Reject duplications in the list of parents.
794 if (list_member_oid(inheritOids, parentOid))
795 ereport(ERROR,
796 (errcode(ERRCODE_DUPLICATE_TABLE),
797 errmsg("relation \"%s\" would be inherited from more than once",
798 get_rel_name(parentOid))));
800 inheritOids = lappend_oid(inheritOids, parentOid);
804 * Select tablespace to use: an explicitly indicated one, or (in the case
805 * of a partitioned table) the parent's, if it has one.
807 if (stmt->tablespacename)
809 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
811 if (partitioned && tablespaceId == MyDatabaseTableSpace)
812 ereport(ERROR,
813 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
814 errmsg("cannot specify default tablespace for partitioned relations")));
816 else if (stmt->partbound)
818 Assert(list_length(inheritOids) == 1);
819 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
821 else
822 tablespaceId = InvalidOid;
824 /* still nothing? use the default */
825 if (!OidIsValid(tablespaceId))
826 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
827 partitioned);
829 /* Check permissions except when using database's default */
830 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
832 AclResult aclresult;
834 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
835 ACL_CREATE);
836 if (aclresult != ACLCHECK_OK)
837 aclcheck_error(aclresult, OBJECT_TABLESPACE,
838 get_tablespace_name(tablespaceId));
841 /* In all cases disallow placing user relations in pg_global */
842 if (tablespaceId == GLOBALTABLESPACE_OID)
843 ereport(ERROR,
844 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
845 errmsg("only shared relations can be placed in pg_global tablespace")));
847 /* Identify user ID that will own the table */
848 if (!OidIsValid(ownerId))
849 ownerId = GetUserId();
852 * Parse and validate reloptions, if any.
854 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
855 true, false);
857 switch (relkind)
859 case RELKIND_VIEW:
860 (void) view_reloptions(reloptions, true);
861 break;
862 case RELKIND_PARTITIONED_TABLE:
863 (void) partitioned_table_reloptions(reloptions, true);
864 break;
865 default:
866 (void) heap_reloptions(relkind, reloptions, true);
869 if (stmt->ofTypename)
871 AclResult aclresult;
873 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
875 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
876 if (aclresult != ACLCHECK_OK)
877 aclcheck_error_type(aclresult, ofTypeId);
879 else
880 ofTypeId = InvalidOid;
883 * Look up inheritance ancestors and generate relation schema, including
884 * inherited attributes. (Note that stmt->tableElts is destructively
885 * modified by MergeAttributes.)
887 stmt->tableElts =
888 MergeAttributes(stmt->tableElts, inheritOids,
889 stmt->relation->relpersistence,
890 stmt->partbound != NULL,
891 &old_constraints, &old_notnulls);
894 * Create a tuple descriptor from the relation schema. Note that this
895 * deals with column names, types, and in-descriptor NOT NULL flags, but
896 * not default values, NOT NULL or CHECK constraints; we handle those
897 * below.
899 descriptor = BuildDescForRelation(stmt->tableElts);
902 * Find columns with default values and prepare for insertion of the
903 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
904 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
905 * while raw defaults go into a list of RawColumnDefault structs that will
906 * be processed by AddRelationNewConstraints. (We can't deal with raw
907 * expressions until we can do transformExpr.)
909 * We can set the atthasdef flags now in the tuple descriptor; this just
910 * saves StoreAttrDefault from having to do an immediate update of the
911 * pg_attribute rows.
913 rawDefaults = NIL;
914 cookedDefaults = NIL;
915 attnum = 0;
917 foreach(listptr, stmt->tableElts)
919 ColumnDef *colDef = lfirst(listptr);
920 Form_pg_attribute attr;
922 attnum++;
923 attr = TupleDescAttr(descriptor, attnum - 1);
925 if (colDef->raw_default != NULL)
927 RawColumnDefault *rawEnt;
929 Assert(colDef->cooked_default == NULL);
931 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
932 rawEnt->attnum = attnum;
933 rawEnt->raw_default = colDef->raw_default;
934 rawEnt->missingMode = false;
935 rawEnt->generated = colDef->generated;
936 rawDefaults = lappend(rawDefaults, rawEnt);
937 attr->atthasdef = true;
939 else if (colDef->cooked_default != NULL)
941 CookedConstraint *cooked;
943 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
944 cooked->contype = CONSTR_DEFAULT;
945 cooked->conoid = InvalidOid; /* until created */
946 cooked->name = NULL;
947 cooked->attnum = attnum;
948 cooked->expr = colDef->cooked_default;
949 cooked->skip_validation = false;
950 cooked->is_local = true; /* not used for defaults */
951 cooked->inhcount = 0; /* ditto */
952 cooked->is_no_inherit = false;
953 cookedDefaults = lappend(cookedDefaults, cooked);
954 attr->atthasdef = true;
959 * Select access method to use: an explicitly indicated one, or (in the
960 * case of a partitioned table) the parent's, if it has one.
962 if (stmt->accessMethod != NULL)
963 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
964 else if (stmt->partbound)
966 Assert(list_length(inheritOids) == 1);
967 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
969 else
970 accessMethodId = InvalidOid;
972 /* still nothing? use the default */
973 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
974 accessMethodId = get_table_am_oid(default_table_access_method, false);
977 * Create the relation. Inherited defaults and constraints are passed in
978 * for immediate handling --- since they don't need parsing, they can be
979 * stored immediately.
981 relationId = heap_create_with_catalog(relname,
982 namespaceId,
983 tablespaceId,
984 InvalidOid,
985 InvalidOid,
986 ofTypeId,
987 ownerId,
988 accessMethodId,
989 descriptor,
990 list_concat(cookedDefaults,
991 old_constraints),
992 relkind,
993 stmt->relation->relpersistence,
994 false,
995 false,
996 stmt->oncommit,
997 reloptions,
998 true,
999 allowSystemTableMods,
1000 false,
1001 InvalidOid,
1002 typaddress);
1005 * We must bump the command counter to make the newly-created relation
1006 * tuple visible for opening.
1008 CommandCounterIncrement();
1011 * Open the new relation and acquire exclusive lock on it. This isn't
1012 * really necessary for locking out other backends (since they can't see
1013 * the new rel anyway until we commit), but it keeps the lock manager from
1014 * complaining about deadlock risks.
1016 rel = relation_open(relationId, AccessExclusiveLock);
1019 * Now add any newly specified column default and generation expressions
1020 * to the new relation. These are passed to us in the form of raw
1021 * parsetrees; we need to transform them to executable expression trees
1022 * before they can be added. The most convenient way to do that is to
1023 * apply the parser's transformExpr routine, but transformExpr doesn't
1024 * work unless we have a pre-existing relation. So, the transformation has
1025 * to be postponed to this final step of CREATE TABLE.
1027 * This needs to be before processing the partitioning clauses because
1028 * those could refer to generated columns.
1030 if (rawDefaults)
1031 AddRelationNewConstraints(rel, rawDefaults, NIL,
1032 true, true, false, queryString);
1035 * Make column generation expressions visible for use by partitioning.
1037 CommandCounterIncrement();
1039 /* Process and store partition bound, if any. */
1040 if (stmt->partbound)
1042 PartitionBoundSpec *bound;
1043 ParseState *pstate;
1044 Oid parentId = linitial_oid(inheritOids),
1045 defaultPartOid;
1046 Relation parent,
1047 defaultRel = NULL;
1048 ParseNamespaceItem *nsitem;
1050 /* Already have strong enough lock on the parent */
1051 parent = table_open(parentId, NoLock);
1054 * We are going to try to validate the partition bound specification
1055 * against the partition key of parentRel, so it better have one.
1057 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1058 ereport(ERROR,
1059 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1060 errmsg("\"%s\" is not partitioned",
1061 RelationGetRelationName(parent))));
1064 * The partition constraint of the default partition depends on the
1065 * partition bounds of every other partition. It is possible that
1066 * another backend might be about to execute a query on the default
1067 * partition table, and that the query relies on previously cached
1068 * default partition constraints. We must therefore take a table lock
1069 * strong enough to prevent all queries on the default partition from
1070 * proceeding until we commit and send out a shared-cache-inval notice
1071 * that will make them update their index lists.
1073 * Order of locking: The relation being added won't be visible to
1074 * other backends until it is committed, hence here in
1075 * DefineRelation() the order of locking the default partition and the
1076 * relation being added does not matter. But at all other places we
1077 * need to lock the default relation before we lock the relation being
1078 * added or removed i.e. we should take the lock in same order at all
1079 * the places such that lock parent, lock default partition and then
1080 * lock the partition so as to avoid a deadlock.
1082 defaultPartOid =
1083 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1084 true));
1085 if (OidIsValid(defaultPartOid))
1086 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1088 /* Transform the bound values */
1089 pstate = make_parsestate(NULL);
1090 pstate->p_sourcetext = queryString;
1093 * Add an nsitem containing this relation, so that transformExpr
1094 * called on partition bound expressions is able to report errors
1095 * using a proper context.
1097 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1098 NULL, false, false);
1099 addNSItemToQuery(pstate, nsitem, false, true, true);
1101 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1104 * Check first that the new partition's bound is valid and does not
1105 * overlap with any of existing partitions of the parent.
1107 check_new_partition_bound(relname, parent, bound, pstate);
1110 * If the default partition exists, its partition constraints will
1111 * change after the addition of this new partition such that it won't
1112 * allow any row that qualifies for this new partition. So, check that
1113 * the existing data in the default partition satisfies the constraint
1114 * as it will exist after adding this partition.
1116 if (OidIsValid(defaultPartOid))
1118 check_default_partition_contents(parent, defaultRel, bound);
1119 /* Keep the lock until commit. */
1120 table_close(defaultRel, NoLock);
1123 /* Update the pg_class entry. */
1124 StorePartitionBound(rel, parent, bound);
1126 table_close(parent, NoLock);
1129 /* Store inheritance information for new rel. */
1130 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1133 * Process the partitioning specification (if any) and store the partition
1134 * key information into the catalog.
1136 if (partitioned)
1138 ParseState *pstate;
1139 int partnatts;
1140 AttrNumber partattrs[PARTITION_MAX_KEYS];
1141 Oid partopclass[PARTITION_MAX_KEYS];
1142 Oid partcollation[PARTITION_MAX_KEYS];
1143 List *partexprs = NIL;
1145 pstate = make_parsestate(NULL);
1146 pstate->p_sourcetext = queryString;
1148 partnatts = list_length(stmt->partspec->partParams);
1150 /* Protect fixed-size arrays here and in executor */
1151 if (partnatts > PARTITION_MAX_KEYS)
1152 ereport(ERROR,
1153 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1154 errmsg("cannot partition using more than %d columns",
1155 PARTITION_MAX_KEYS)));
1158 * We need to transform the raw parsetrees corresponding to partition
1159 * expressions into executable expression trees. Like column defaults
1160 * and CHECK constraints, we could not have done the transformation
1161 * earlier.
1163 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1165 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1166 partattrs, &partexprs, partopclass,
1167 partcollation, stmt->partspec->strategy);
1169 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1170 partexprs,
1171 partopclass, partcollation);
1173 /* make it all visible */
1174 CommandCounterIncrement();
1178 * If we're creating a partition, create now all the indexes, triggers,
1179 * FKs defined in the parent.
1181 * We can't do it earlier, because DefineIndex wants to know the partition
1182 * key which we just stored.
1184 if (stmt->partbound)
1186 Oid parentId = linitial_oid(inheritOids);
1187 Relation parent;
1188 List *idxlist;
1189 ListCell *cell;
1191 /* Already have strong enough lock on the parent */
1192 parent = table_open(parentId, NoLock);
1193 idxlist = RelationGetIndexList(parent);
1196 * For each index in the parent table, create one in the partition
1198 foreach(cell, idxlist)
1200 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1201 AttrMap *attmap;
1202 IndexStmt *idxstmt;
1203 Oid constraintOid;
1205 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1207 if (idxRel->rd_index->indisunique)
1208 ereport(ERROR,
1209 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1210 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1211 RelationGetRelationName(parent)),
1212 errdetail("Table \"%s\" contains indexes that are unique.",
1213 RelationGetRelationName(parent))));
1214 else
1216 index_close(idxRel, AccessShareLock);
1217 continue;
1221 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1222 RelationGetDescr(parent),
1223 false);
1224 idxstmt =
1225 generateClonedIndexStmt(NULL, idxRel,
1226 attmap, &constraintOid);
1227 DefineIndex(RelationGetRelid(rel),
1228 idxstmt,
1229 InvalidOid,
1230 RelationGetRelid(idxRel),
1231 constraintOid,
1233 false, false, false, false, false);
1235 index_close(idxRel, AccessShareLock);
1238 list_free(idxlist);
1241 * If there are any row-level triggers, clone them to the new
1242 * partition.
1244 if (parent->trigdesc != NULL)
1245 CloneRowTriggersToPartition(parent, rel);
1248 * And foreign keys too. Note that because we're freshly creating the
1249 * table, there is no need to verify these new constraints.
1251 CloneForeignKeyConstraints(NULL, parent, rel);
1253 table_close(parent, NoLock);
1257 * Now add any newly specified CHECK constraints to the new relation. Same
1258 * as for defaults above, but these need to come after partitioning is set
1259 * up.
1261 if (stmt->constraints)
1262 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1263 true, true, false, queryString);
1266 * Finally, merge the not-null constraints that are declared directly with
1267 * those that come from parent relations (making sure to count inheritance
1268 * appropriately for each), create them, and set the attnotnull flag on
1269 * columns that don't yet have it.
1271 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1272 old_notnulls);
1273 foreach(listptr, nncols)
1274 set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1276 ObjectAddressSet(address, RelationRelationId, relationId);
1279 * Clean up. We keep lock on new relation (although it shouldn't be
1280 * visible to anyone else anyway, until commit).
1282 relation_close(rel, NoLock);
1284 return address;
1288 * BuildDescForRelation
1290 * Given a list of ColumnDef nodes, build a TupleDesc.
1292 * Note: tdtypeid will need to be filled in later on.
1294 TupleDesc
1295 BuildDescForRelation(const List *columns)
1297 int natts;
1298 AttrNumber attnum;
1299 ListCell *l;
1300 TupleDesc desc;
1301 bool has_not_null;
1302 char *attname;
1303 Oid atttypid;
1304 int32 atttypmod;
1305 Oid attcollation;
1306 int attdim;
1309 * allocate a new tuple descriptor
1311 natts = list_length(columns);
1312 desc = CreateTemplateTupleDesc(natts);
1313 has_not_null = false;
1315 attnum = 0;
1317 foreach(l, columns)
1319 ColumnDef *entry = lfirst(l);
1320 AclResult aclresult;
1321 Form_pg_attribute att;
1324 * for each entry in the list, get the name and type information from
1325 * the list and have TupleDescInitEntry fill in the attribute
1326 * information we need.
1328 attnum++;
1330 attname = entry->colname;
1331 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1333 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1334 if (aclresult != ACLCHECK_OK)
1335 aclcheck_error_type(aclresult, atttypid);
1337 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1338 attdim = list_length(entry->typeName->arrayBounds);
1339 if (attdim > PG_INT16_MAX)
1340 ereport(ERROR,
1341 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1342 errmsg("too many array dimensions"));
1344 if (entry->typeName->setof)
1345 ereport(ERROR,
1346 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1347 errmsg("column \"%s\" cannot be declared SETOF",
1348 attname)));
1350 TupleDescInitEntry(desc, attnum, attname,
1351 atttypid, atttypmod, attdim);
1352 att = TupleDescAttr(desc, attnum - 1);
1354 /* Override TupleDescInitEntry's settings as requested */
1355 TupleDescInitEntryCollation(desc, attnum, attcollation);
1357 /* Fill in additional stuff not handled by TupleDescInitEntry */
1358 att->attnotnull = entry->is_not_null;
1359 has_not_null |= entry->is_not_null;
1360 att->attislocal = entry->is_local;
1361 att->attinhcount = entry->inhcount;
1362 att->attidentity = entry->identity;
1363 att->attgenerated = entry->generated;
1364 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1365 if (entry->storage)
1366 att->attstorage = entry->storage;
1367 else if (entry->storage_name)
1368 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1371 if (has_not_null)
1373 TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1375 constr->has_not_null = true;
1376 constr->has_generated_stored = false;
1377 constr->defval = NULL;
1378 constr->missing = NULL;
1379 constr->num_defval = 0;
1380 constr->check = NULL;
1381 constr->num_check = 0;
1382 desc->constr = constr;
1384 else
1386 desc->constr = NULL;
1389 return desc;
1393 * Emit the right error or warning message for a "DROP" command issued on a
1394 * non-existent relation
1396 static void
1397 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1399 const struct dropmsgstrings *rentry;
1401 if (rel->schemaname != NULL &&
1402 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1404 if (!missing_ok)
1406 ereport(ERROR,
1407 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1408 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1410 else
1412 ereport(NOTICE,
1413 (errmsg("schema \"%s\" does not exist, skipping",
1414 rel->schemaname)));
1416 return;
1419 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1421 if (rentry->kind == rightkind)
1423 if (!missing_ok)
1425 ereport(ERROR,
1426 (errcode(rentry->nonexistent_code),
1427 errmsg(rentry->nonexistent_msg, rel->relname)));
1429 else
1431 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1432 break;
1437 Assert(rentry->kind != '\0'); /* Should be impossible */
1441 * Emit the right error message for a "DROP" command issued on a
1442 * relation of the wrong type
1444 static void
1445 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1447 const struct dropmsgstrings *rentry;
1448 const struct dropmsgstrings *wentry;
1450 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1451 if (rentry->kind == rightkind)
1452 break;
1453 Assert(rentry->kind != '\0');
1455 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1456 if (wentry->kind == wrongkind)
1457 break;
1458 /* wrongkind could be something we don't have in our table... */
1460 ereport(ERROR,
1461 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1462 errmsg(rentry->nota_msg, relname),
1463 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1467 * RemoveRelations
1468 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1469 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1471 void
1472 RemoveRelations(DropStmt *drop)
1474 ObjectAddresses *objects;
1475 char relkind;
1476 ListCell *cell;
1477 int flags = 0;
1478 LOCKMODE lockmode = AccessExclusiveLock;
1480 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1481 if (drop->concurrent)
1484 * Note that for temporary relations this lock may get upgraded later
1485 * on, but as no other session can access a temporary relation, this
1486 * is actually fine.
1488 lockmode = ShareUpdateExclusiveLock;
1489 Assert(drop->removeType == OBJECT_INDEX);
1490 if (list_length(drop->objects) != 1)
1491 ereport(ERROR,
1492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1493 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1494 if (drop->behavior == DROP_CASCADE)
1495 ereport(ERROR,
1496 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1497 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1501 * First we identify all the relations, then we delete them in a single
1502 * performMultipleDeletions() call. This is to avoid unwanted DROP
1503 * RESTRICT errors if one of the relations depends on another.
1506 /* Determine required relkind */
1507 switch (drop->removeType)
1509 case OBJECT_TABLE:
1510 relkind = RELKIND_RELATION;
1511 break;
1513 case OBJECT_INDEX:
1514 relkind = RELKIND_INDEX;
1515 break;
1517 case OBJECT_SEQUENCE:
1518 relkind = RELKIND_SEQUENCE;
1519 break;
1521 case OBJECT_VIEW:
1522 relkind = RELKIND_VIEW;
1523 break;
1525 case OBJECT_MATVIEW:
1526 relkind = RELKIND_MATVIEW;
1527 break;
1529 case OBJECT_FOREIGN_TABLE:
1530 relkind = RELKIND_FOREIGN_TABLE;
1531 break;
1533 default:
1534 elog(ERROR, "unrecognized drop object type: %d",
1535 (int) drop->removeType);
1536 relkind = 0; /* keep compiler quiet */
1537 break;
1540 /* Lock and validate each relation; build a list of object addresses */
1541 objects = new_object_addresses();
1543 foreach(cell, drop->objects)
1545 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1546 Oid relOid;
1547 ObjectAddress obj;
1548 struct DropRelationCallbackState state;
1551 * These next few steps are a great deal like relation_openrv, but we
1552 * don't bother building a relcache entry since we don't need it.
1554 * Check for shared-cache-inval messages before trying to access the
1555 * relation. This is needed to cover the case where the name
1556 * identifies a rel that has been dropped and recreated since the
1557 * start of our transaction: if we don't flush the old syscache entry,
1558 * then we'll latch onto that entry and suffer an error later.
1560 AcceptInvalidationMessages();
1562 /* Look up the appropriate relation using namespace search. */
1563 state.expected_relkind = relkind;
1564 state.heap_lockmode = drop->concurrent ?
1565 ShareUpdateExclusiveLock : AccessExclusiveLock;
1566 /* We must initialize these fields to show that no locks are held: */
1567 state.heapOid = InvalidOid;
1568 state.partParentOid = InvalidOid;
1570 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1571 RangeVarCallbackForDropRelation,
1572 (void *) &state);
1574 /* Not there? */
1575 if (!OidIsValid(relOid))
1577 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1578 continue;
1582 * Decide if concurrent mode needs to be used here or not. The
1583 * callback retrieved the rel's persistence for us.
1585 if (drop->concurrent &&
1586 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1588 Assert(list_length(drop->objects) == 1 &&
1589 drop->removeType == OBJECT_INDEX);
1590 flags |= PERFORM_DELETION_CONCURRENTLY;
1594 * Concurrent index drop cannot be used with partitioned indexes,
1595 * either.
1597 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1598 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1599 ereport(ERROR,
1600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1601 errmsg("cannot drop partitioned index \"%s\" concurrently",
1602 rel->relname)));
1605 * If we're told to drop a partitioned index, we must acquire lock on
1606 * all the children of its parent partitioned table before proceeding.
1607 * Otherwise we'd try to lock the child index partitions before their
1608 * tables, leading to potential deadlock against other sessions that
1609 * will lock those objects in the other order.
1611 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1612 (void) find_all_inheritors(state.heapOid,
1613 state.heap_lockmode,
1614 NULL);
1616 /* OK, we're ready to delete this one */
1617 obj.classId = RelationRelationId;
1618 obj.objectId = relOid;
1619 obj.objectSubId = 0;
1621 add_exact_object_address(&obj, objects);
1624 performMultipleDeletions(objects, drop->behavior, flags);
1626 free_object_addresses(objects);
1630 * Before acquiring a table lock, check whether we have sufficient rights.
1631 * In the case of DROP INDEX, also try to lock the table before the index.
1632 * Also, if the table to be dropped is a partition, we try to lock the parent
1633 * first.
1635 static void
1636 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1637 void *arg)
1639 HeapTuple tuple;
1640 struct DropRelationCallbackState *state;
1641 char expected_relkind;
1642 bool is_partition;
1643 Form_pg_class classform;
1644 LOCKMODE heap_lockmode;
1645 bool invalid_system_index = false;
1647 state = (struct DropRelationCallbackState *) arg;
1648 heap_lockmode = state->heap_lockmode;
1651 * If we previously locked some other index's heap, and the name we're
1652 * looking up no longer refers to that relation, release the now-useless
1653 * lock.
1655 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1657 UnlockRelationOid(state->heapOid, heap_lockmode);
1658 state->heapOid = InvalidOid;
1662 * Similarly, if we previously locked some other partition's heap, and the
1663 * name we're looking up no longer refers to that relation, release the
1664 * now-useless lock.
1666 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1668 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1669 state->partParentOid = InvalidOid;
1672 /* Didn't find a relation, so no need for locking or permission checks. */
1673 if (!OidIsValid(relOid))
1674 return;
1676 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1677 if (!HeapTupleIsValid(tuple))
1678 return; /* concurrently dropped, so nothing to do */
1679 classform = (Form_pg_class) GETSTRUCT(tuple);
1680 is_partition = classform->relispartition;
1682 /* Pass back some data to save lookups in RemoveRelations */
1683 state->actual_relkind = classform->relkind;
1684 state->actual_relpersistence = classform->relpersistence;
1687 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1688 * but RemoveRelations() can only pass one relkind for a given relation.
1689 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1690 * That means we must be careful before giving the wrong type error when
1691 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1692 * exists with indexes.
1694 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1695 expected_relkind = RELKIND_RELATION;
1696 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1697 expected_relkind = RELKIND_INDEX;
1698 else
1699 expected_relkind = classform->relkind;
1701 if (state->expected_relkind != expected_relkind)
1702 DropErrorMsgWrongType(rel->relname, classform->relkind,
1703 state->expected_relkind);
1705 /* Allow DROP to either table owner or schema owner */
1706 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1707 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1708 aclcheck_error(ACLCHECK_NOT_OWNER,
1709 get_relkind_objtype(classform->relkind),
1710 rel->relname);
1713 * Check the case of a system index that might have been invalidated by a
1714 * failed concurrent process and allow its drop. For the time being, this
1715 * only concerns indexes of toast relations that became invalid during a
1716 * REINDEX CONCURRENTLY process.
1718 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1720 HeapTuple locTuple;
1721 Form_pg_index indexform;
1722 bool indisvalid;
1724 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1725 if (!HeapTupleIsValid(locTuple))
1727 ReleaseSysCache(tuple);
1728 return;
1731 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1732 indisvalid = indexform->indisvalid;
1733 ReleaseSysCache(locTuple);
1735 /* Mark object as being an invalid index of system catalogs */
1736 if (!indisvalid)
1737 invalid_system_index = true;
1740 /* In the case of an invalid index, it is fine to bypass this check */
1741 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1742 ereport(ERROR,
1743 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1744 errmsg("permission denied: \"%s\" is a system catalog",
1745 rel->relname)));
1747 ReleaseSysCache(tuple);
1750 * In DROP INDEX, attempt to acquire lock on the parent table before
1751 * locking the index. index_drop() will need this anyway, and since
1752 * regular queries lock tables before their indexes, we risk deadlock if
1753 * we do it the other way around. No error if we don't find a pg_index
1754 * entry, though --- the relation may have been dropped. Note that this
1755 * code will execute for either plain or partitioned indexes.
1757 if (expected_relkind == RELKIND_INDEX &&
1758 relOid != oldRelOid)
1760 state->heapOid = IndexGetRelation(relOid, true);
1761 if (OidIsValid(state->heapOid))
1762 LockRelationOid(state->heapOid, heap_lockmode);
1766 * Similarly, if the relation is a partition, we must acquire lock on its
1767 * parent before locking the partition. That's because queries lock the
1768 * parent before its partitions, so we risk deadlock if we do it the other
1769 * way around.
1771 if (is_partition && relOid != oldRelOid)
1773 state->partParentOid = get_partition_parent(relOid, true);
1774 if (OidIsValid(state->partParentOid))
1775 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1780 * ExecuteTruncate
1781 * Executes a TRUNCATE command.
1783 * This is a multi-relation truncate. We first open and grab exclusive
1784 * lock on all relations involved, checking permissions and otherwise
1785 * verifying that the relation is OK for truncation. Note that if relations
1786 * are foreign tables, at this stage, we have not yet checked that their
1787 * foreign data in external data sources are OK for truncation. These are
1788 * checked when foreign data are actually truncated later. In CASCADE mode,
1789 * relations having FK references to the targeted relations are automatically
1790 * added to the group; in RESTRICT mode, we check that all FK references are
1791 * internal to the group that's being truncated. Finally all the relations
1792 * are truncated and reindexed.
1794 void
1795 ExecuteTruncate(TruncateStmt *stmt)
1797 List *rels = NIL;
1798 List *relids = NIL;
1799 List *relids_logged = NIL;
1800 ListCell *cell;
1803 * Open, exclusive-lock, and check all the explicitly-specified relations
1805 foreach(cell, stmt->relations)
1807 RangeVar *rv = lfirst(cell);
1808 Relation rel;
1809 bool recurse = rv->inh;
1810 Oid myrelid;
1811 LOCKMODE lockmode = AccessExclusiveLock;
1813 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1814 0, RangeVarCallbackForTruncate,
1815 NULL);
1817 /* don't throw error for "TRUNCATE foo, foo" */
1818 if (list_member_oid(relids, myrelid))
1819 continue;
1821 /* open the relation, we already hold a lock on it */
1822 rel = table_open(myrelid, NoLock);
1825 * RangeVarGetRelidExtended() has done most checks with its callback,
1826 * but other checks with the now-opened Relation remain.
1828 truncate_check_activity(rel);
1830 rels = lappend(rels, rel);
1831 relids = lappend_oid(relids, myrelid);
1833 /* Log this relation only if needed for logical decoding */
1834 if (RelationIsLogicallyLogged(rel))
1835 relids_logged = lappend_oid(relids_logged, myrelid);
1837 if (recurse)
1839 ListCell *child;
1840 List *children;
1842 children = find_all_inheritors(myrelid, lockmode, NULL);
1844 foreach(child, children)
1846 Oid childrelid = lfirst_oid(child);
1848 if (list_member_oid(relids, childrelid))
1849 continue;
1851 /* find_all_inheritors already got lock */
1852 rel = table_open(childrelid, NoLock);
1855 * It is possible that the parent table has children that are
1856 * temp tables of other backends. We cannot safely access
1857 * such tables (because of buffering issues), and the best
1858 * thing to do is to silently ignore them. Note that this
1859 * check is the same as one of the checks done in
1860 * truncate_check_activity() called below, still it is kept
1861 * here for simplicity.
1863 if (RELATION_IS_OTHER_TEMP(rel))
1865 table_close(rel, lockmode);
1866 continue;
1870 * Inherited TRUNCATE commands perform access permission
1871 * checks on the parent table only. So we skip checking the
1872 * children's permissions and don't call
1873 * truncate_check_perms() here.
1875 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1876 truncate_check_activity(rel);
1878 rels = lappend(rels, rel);
1879 relids = lappend_oid(relids, childrelid);
1881 /* Log this relation only if needed for logical decoding */
1882 if (RelationIsLogicallyLogged(rel))
1883 relids_logged = lappend_oid(relids_logged, childrelid);
1886 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1887 ereport(ERROR,
1888 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1889 errmsg("cannot truncate only a partitioned table"),
1890 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1893 ExecuteTruncateGuts(rels, relids, relids_logged,
1894 stmt->behavior, stmt->restart_seqs, false);
1896 /* And close the rels */
1897 foreach(cell, rels)
1899 Relation rel = (Relation) lfirst(cell);
1901 table_close(rel, NoLock);
1906 * ExecuteTruncateGuts
1908 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1909 * command (see above) as well as replication subscribers that execute a
1910 * replicated TRUNCATE action.
1912 * explicit_rels is the list of Relations to truncate that the command
1913 * specified. relids is the list of Oids corresponding to explicit_rels.
1914 * relids_logged is the list of Oids (a subset of relids) that require
1915 * WAL-logging. This is all a bit redundant, but the existing callers have
1916 * this information handy in this form.
1918 void
1919 ExecuteTruncateGuts(List *explicit_rels,
1920 List *relids,
1921 List *relids_logged,
1922 DropBehavior behavior, bool restart_seqs,
1923 bool run_as_table_owner)
1925 List *rels;
1926 List *seq_relids = NIL;
1927 HTAB *ft_htab = NULL;
1928 EState *estate;
1929 ResultRelInfo *resultRelInfos;
1930 ResultRelInfo *resultRelInfo;
1931 SubTransactionId mySubid;
1932 ListCell *cell;
1933 Oid *logrelids;
1936 * Check the explicitly-specified relations.
1938 * In CASCADE mode, suck in all referencing relations as well. This
1939 * requires multiple iterations to find indirectly-dependent relations. At
1940 * each phase, we need to exclusive-lock new rels before looking for their
1941 * dependencies, else we might miss something. Also, we check each rel as
1942 * soon as we open it, to avoid a faux pas such as holding lock for a long
1943 * time on a rel we have no permissions for.
1945 rels = list_copy(explicit_rels);
1946 if (behavior == DROP_CASCADE)
1948 for (;;)
1950 List *newrelids;
1952 newrelids = heap_truncate_find_FKs(relids);
1953 if (newrelids == NIL)
1954 break; /* nothing else to add */
1956 foreach(cell, newrelids)
1958 Oid relid = lfirst_oid(cell);
1959 Relation rel;
1961 rel = table_open(relid, AccessExclusiveLock);
1962 ereport(NOTICE,
1963 (errmsg("truncate cascades to table \"%s\"",
1964 RelationGetRelationName(rel))));
1965 truncate_check_rel(relid, rel->rd_rel);
1966 truncate_check_perms(relid, rel->rd_rel);
1967 truncate_check_activity(rel);
1968 rels = lappend(rels, rel);
1969 relids = lappend_oid(relids, relid);
1971 /* Log this relation only if needed for logical decoding */
1972 if (RelationIsLogicallyLogged(rel))
1973 relids_logged = lappend_oid(relids_logged, relid);
1979 * Check foreign key references. In CASCADE mode, this should be
1980 * unnecessary since we just pulled in all the references; but as a
1981 * cross-check, do it anyway if in an Assert-enabled build.
1983 #ifdef USE_ASSERT_CHECKING
1984 heap_truncate_check_FKs(rels, false);
1985 #else
1986 if (behavior == DROP_RESTRICT)
1987 heap_truncate_check_FKs(rels, false);
1988 #endif
1991 * If we are asked to restart sequences, find all the sequences, lock them
1992 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1993 * We want to do this early since it's pointless to do all the truncation
1994 * work only to fail on sequence permissions.
1996 if (restart_seqs)
1998 foreach(cell, rels)
2000 Relation rel = (Relation) lfirst(cell);
2001 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2002 ListCell *seqcell;
2004 foreach(seqcell, seqlist)
2006 Oid seq_relid = lfirst_oid(seqcell);
2007 Relation seq_rel;
2009 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2011 /* This check must match AlterSequence! */
2012 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2013 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2014 RelationGetRelationName(seq_rel));
2016 seq_relids = lappend_oid(seq_relids, seq_relid);
2018 relation_close(seq_rel, NoLock);
2023 /* Prepare to catch AFTER triggers. */
2024 AfterTriggerBeginQuery();
2027 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2028 * each relation. We don't need to call ExecOpenIndices, though.
2030 * We put the ResultRelInfos in the es_opened_result_relations list, even
2031 * though we don't have a range table and don't populate the
2032 * es_result_relations array. That's a bit bogus, but it's enough to make
2033 * ExecGetTriggerResultRel() find them.
2035 estate = CreateExecutorState();
2036 resultRelInfos = (ResultRelInfo *)
2037 palloc(list_length(rels) * sizeof(ResultRelInfo));
2038 resultRelInfo = resultRelInfos;
2039 foreach(cell, rels)
2041 Relation rel = (Relation) lfirst(cell);
2043 InitResultRelInfo(resultRelInfo,
2044 rel,
2045 0, /* dummy rangetable index */
2046 NULL,
2048 estate->es_opened_result_relations =
2049 lappend(estate->es_opened_result_relations, resultRelInfo);
2050 resultRelInfo++;
2054 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2055 * truncating (this is because one of them might throw an error). Also, if
2056 * we were to allow them to prevent statement execution, that would need
2057 * to be handled here.
2059 resultRelInfo = resultRelInfos;
2060 foreach(cell, rels)
2062 UserContext ucxt;
2064 if (run_as_table_owner)
2065 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2066 &ucxt);
2067 ExecBSTruncateTriggers(estate, resultRelInfo);
2068 if (run_as_table_owner)
2069 RestoreUserContext(&ucxt);
2070 resultRelInfo++;
2074 * OK, truncate each table.
2076 mySubid = GetCurrentSubTransactionId();
2078 foreach(cell, rels)
2080 Relation rel = (Relation) lfirst(cell);
2082 /* Skip partitioned tables as there is nothing to do */
2083 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2084 continue;
2087 * Build the lists of foreign tables belonging to each foreign server
2088 * and pass each list to the foreign data wrapper's callback function,
2089 * so that each server can truncate its all foreign tables in bulk.
2090 * Each list is saved as a single entry in a hash table that uses the
2091 * server OID as lookup key.
2093 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2095 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2096 bool found;
2097 ForeignTruncateInfo *ft_info;
2099 /* First time through, initialize hashtable for foreign tables */
2100 if (!ft_htab)
2102 HASHCTL hctl;
2104 memset(&hctl, 0, sizeof(HASHCTL));
2105 hctl.keysize = sizeof(Oid);
2106 hctl.entrysize = sizeof(ForeignTruncateInfo);
2107 hctl.hcxt = CurrentMemoryContext;
2109 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2110 32, /* start small and extend */
2111 &hctl,
2112 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2115 /* Find or create cached entry for the foreign table */
2116 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2117 if (!found)
2118 ft_info->rels = NIL;
2121 * Save the foreign table in the entry of the server that the
2122 * foreign table belongs to.
2124 ft_info->rels = lappend(ft_info->rels, rel);
2125 continue;
2129 * Normally, we need a transaction-safe truncation here. However, if
2130 * the table was either created in the current (sub)transaction or has
2131 * a new relfilenumber in the current (sub)transaction, then we can
2132 * just truncate it in-place, because a rollback would cause the whole
2133 * table or the current physical file to be thrown away anyway.
2135 if (rel->rd_createSubid == mySubid ||
2136 rel->rd_newRelfilelocatorSubid == mySubid)
2138 /* Immediate, non-rollbackable truncation is OK */
2139 heap_truncate_one_rel(rel);
2141 else
2143 Oid heap_relid;
2144 Oid toast_relid;
2145 ReindexParams reindex_params = {0};
2148 * This effectively deletes all rows in the table, and may be done
2149 * in a serializable transaction. In that case we must record a
2150 * rw-conflict in to this transaction from each transaction
2151 * holding a predicate lock on the table.
2153 CheckTableForSerializableConflictIn(rel);
2156 * Need the full transaction-safe pushups.
2158 * Create a new empty storage file for the relation, and assign it
2159 * as the relfilenumber value. The old storage file is scheduled
2160 * for deletion at commit.
2162 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2164 heap_relid = RelationGetRelid(rel);
2167 * The same for the toast table, if any.
2169 toast_relid = rel->rd_rel->reltoastrelid;
2170 if (OidIsValid(toast_relid))
2172 Relation toastrel = relation_open(toast_relid,
2173 AccessExclusiveLock);
2175 RelationSetNewRelfilenumber(toastrel,
2176 toastrel->rd_rel->relpersistence);
2177 table_close(toastrel, NoLock);
2181 * Reconstruct the indexes to match, and we're done.
2183 reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2184 &reindex_params);
2187 pgstat_count_truncate(rel);
2190 /* Now go through the hash table, and truncate foreign tables */
2191 if (ft_htab)
2193 ForeignTruncateInfo *ft_info;
2194 HASH_SEQ_STATUS seq;
2196 hash_seq_init(&seq, ft_htab);
2198 PG_TRY();
2200 while ((ft_info = hash_seq_search(&seq)) != NULL)
2202 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2204 /* truncate_check_rel() has checked that already */
2205 Assert(routine->ExecForeignTruncate != NULL);
2207 routine->ExecForeignTruncate(ft_info->rels,
2208 behavior,
2209 restart_seqs);
2212 PG_FINALLY();
2214 hash_destroy(ft_htab);
2216 PG_END_TRY();
2220 * Restart owned sequences if we were asked to.
2222 foreach(cell, seq_relids)
2224 Oid seq_relid = lfirst_oid(cell);
2226 ResetSequence(seq_relid);
2230 * Write a WAL record to allow this set of actions to be logically
2231 * decoded.
2233 * Assemble an array of relids so we can write a single WAL record for the
2234 * whole action.
2236 if (relids_logged != NIL)
2238 xl_heap_truncate xlrec;
2239 int i = 0;
2241 /* should only get here if wal_level >= logical */
2242 Assert(XLogLogicalInfoActive());
2244 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2245 foreach(cell, relids_logged)
2246 logrelids[i++] = lfirst_oid(cell);
2248 xlrec.dbId = MyDatabaseId;
2249 xlrec.nrelids = list_length(relids_logged);
2250 xlrec.flags = 0;
2251 if (behavior == DROP_CASCADE)
2252 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2253 if (restart_seqs)
2254 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2256 XLogBeginInsert();
2257 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2258 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2260 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2262 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2266 * Process all AFTER STATEMENT TRUNCATE triggers.
2268 resultRelInfo = resultRelInfos;
2269 foreach(cell, rels)
2271 UserContext ucxt;
2273 if (run_as_table_owner)
2274 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2275 &ucxt);
2276 ExecASTruncateTriggers(estate, resultRelInfo);
2277 if (run_as_table_owner)
2278 RestoreUserContext(&ucxt);
2279 resultRelInfo++;
2282 /* Handle queued AFTER triggers */
2283 AfterTriggerEndQuery(estate);
2285 /* We can clean up the EState now */
2286 FreeExecutorState(estate);
2289 * Close any rels opened by CASCADE (can't do this while EState still
2290 * holds refs)
2292 rels = list_difference_ptr(rels, explicit_rels);
2293 foreach(cell, rels)
2295 Relation rel = (Relation) lfirst(cell);
2297 table_close(rel, NoLock);
2302 * Check that a given relation is safe to truncate. Subroutine for
2303 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2305 static void
2306 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2308 char *relname = NameStr(reltuple->relname);
2311 * Only allow truncate on regular tables, foreign tables using foreign
2312 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2313 * latter are only being included here for the following checks; no
2314 * physical truncation will occur in their case.).
2316 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2318 Oid serverid = GetForeignServerIdByRelId(relid);
2319 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2321 if (!fdwroutine->ExecForeignTruncate)
2322 ereport(ERROR,
2323 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2324 errmsg("cannot truncate foreign table \"%s\"",
2325 relname)));
2327 else if (reltuple->relkind != RELKIND_RELATION &&
2328 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2329 ereport(ERROR,
2330 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2331 errmsg("\"%s\" is not a table", relname)));
2334 * Most system catalogs can't be truncated at all, or at least not unless
2335 * allow_system_table_mods=on. As an exception, however, we allow
2336 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2337 * to change its relfilenode to match the old cluster, and allowing a
2338 * TRUNCATE command to be executed is the easiest way of doing that.
2340 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2341 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2342 ereport(ERROR,
2343 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2344 errmsg("permission denied: \"%s\" is a system catalog",
2345 relname)));
2347 InvokeObjectTruncateHook(relid);
2351 * Check that current user has the permission to truncate given relation.
2353 static void
2354 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2356 char *relname = NameStr(reltuple->relname);
2357 AclResult aclresult;
2359 /* Permissions checks */
2360 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2361 if (aclresult != ACLCHECK_OK)
2362 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2363 relname);
2367 * Set of extra sanity checks to check if a given relation is safe to
2368 * truncate. This is split with truncate_check_rel() as
2369 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2371 static void
2372 truncate_check_activity(Relation rel)
2375 * Don't allow truncate on temp tables of other backends ... their local
2376 * buffer manager is not going to cope.
2378 if (RELATION_IS_OTHER_TEMP(rel))
2379 ereport(ERROR,
2380 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2381 errmsg("cannot truncate temporary tables of other sessions")));
2384 * Also check for active uses of the relation in the current transaction,
2385 * including open scans and pending AFTER trigger events.
2387 CheckTableNotInUse(rel, "TRUNCATE");
2391 * storage_name
2392 * returns the name corresponding to a typstorage/attstorage enum value
2394 static const char *
2395 storage_name(char c)
2397 switch (c)
2399 case TYPSTORAGE_PLAIN:
2400 return "PLAIN";
2401 case TYPSTORAGE_EXTERNAL:
2402 return "EXTERNAL";
2403 case TYPSTORAGE_EXTENDED:
2404 return "EXTENDED";
2405 case TYPSTORAGE_MAIN:
2406 return "MAIN";
2407 default:
2408 return "???";
2412 /*----------
2413 * MergeAttributes
2414 * Returns new schema given initial schema and superclasses.
2416 * Input arguments:
2417 * 'columns' is the column/attribute definition for the table. (It's a list
2418 * of ColumnDef's.) It is destructively changed.
2419 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2420 * 'relpersistence' is the persistence type of the table.
2421 * 'is_partition' tells if the table is a partition.
2423 * Output arguments:
2424 * 'supconstr' receives a list of constraints belonging to the parents,
2425 * updated as necessary to be valid for the child.
2426 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2427 * constraints coming from inheritance parents.
2429 * Return value:
2430 * Completed schema list.
2432 * Notes:
2433 * The order in which the attributes are inherited is very important.
2434 * Intuitively, the inherited attributes should come first. If a table
2435 * inherits from multiple parents, the order of those attributes are
2436 * according to the order of the parents specified in CREATE TABLE.
2438 * Here's an example:
2440 * create table person (name text, age int4, location point);
2441 * create table emp (salary int4, manager text) inherits(person);
2442 * create table student (gpa float8) inherits (person);
2443 * create table stud_emp (percent int4) inherits (emp, student);
2445 * The order of the attributes of stud_emp is:
2447 * person {1:name, 2:age, 3:location}
2448 * / \
2449 * {6:gpa} student emp {4:salary, 5:manager}
2450 * \ /
2451 * stud_emp {7:percent}
2453 * If the same attribute name appears multiple times, then it appears
2454 * in the result table in the proper location for its first appearance.
2456 * Constraints (including not-null constraints) for the child table
2457 * are the union of all relevant constraints, from both the child schema
2458 * and parent tables. In addition, in legacy inheritance, each column that
2459 * appears in a primary key in any of the parents also gets a NOT NULL
2460 * constraint (partitioning doesn't need this, because the PK itself gets
2461 * inherited.)
2463 * The default value for a child column is defined as:
2464 * (1) If the child schema specifies a default, that value is used.
2465 * (2) If neither the child nor any parent specifies a default, then
2466 * the column will not have a default.
2467 * (3) If conflicting defaults are inherited from different parents
2468 * (and not overridden by the child), an error is raised.
2469 * (4) Otherwise the inherited default is used.
2471 * Note that the default-value infrastructure is used for generated
2472 * columns' expressions too, so most of the preceding paragraph applies
2473 * to generation expressions too. We insist that a child column be
2474 * generated if and only if its parent(s) are, but it need not have
2475 * the same generation expression.
2476 *----------
2478 static List *
2479 MergeAttributes(List *columns, const List *supers, char relpersistence,
2480 bool is_partition, List **supconstr, List **supnotnulls)
2482 List *inh_columns = NIL;
2483 List *constraints = NIL;
2484 List *nnconstraints = NIL;
2485 bool have_bogus_defaults = false;
2486 int child_attno;
2487 static Node bogus_marker = {0}; /* marks conflicting defaults */
2488 List *saved_columns = NIL;
2489 ListCell *lc;
2492 * Check for and reject tables with too many columns. We perform this
2493 * check relatively early for two reasons: (a) we don't run the risk of
2494 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2495 * okay if we're processing <= 1600 columns, but could take minutes to
2496 * execute if the user attempts to create a table with hundreds of
2497 * thousands of columns.
2499 * Note that we also need to check that we do not exceed this figure after
2500 * including columns from inherited relations.
2502 if (list_length(columns) > MaxHeapAttributeNumber)
2503 ereport(ERROR,
2504 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2505 errmsg("tables can have at most %d columns",
2506 MaxHeapAttributeNumber)));
2509 * Check for duplicate names in the explicit list of attributes.
2511 * Although we might consider merging such entries in the same way that we
2512 * handle name conflicts for inherited attributes, it seems to make more
2513 * sense to assume such conflicts are errors.
2515 * We don't use foreach() here because we have two nested loops over the
2516 * columns list, with possible element deletions in the inner one. If we
2517 * used foreach_delete_current() it could only fix up the state of one of
2518 * the loops, so it seems cleaner to use looping over list indexes for
2519 * both loops. Note that any deletion will happen beyond where the outer
2520 * loop is, so its index never needs adjustment.
2522 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2524 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2526 if (!is_partition && coldef->typeName == NULL)
2529 * Typed table column option that does not belong to a column from
2530 * the type. This works because the columns from the type come
2531 * first in the list. (We omit this check for partition column
2532 * lists; those are processed separately below.)
2534 ereport(ERROR,
2535 (errcode(ERRCODE_UNDEFINED_COLUMN),
2536 errmsg("column \"%s\" does not exist",
2537 coldef->colname)));
2540 /* restpos scans all entries beyond coldef; incr is in loop body */
2541 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2543 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2545 if (strcmp(coldef->colname, restdef->colname) == 0)
2547 if (coldef->is_from_type)
2550 * merge the column options into the column from the type
2552 coldef->is_not_null = restdef->is_not_null;
2553 coldef->raw_default = restdef->raw_default;
2554 coldef->cooked_default = restdef->cooked_default;
2555 coldef->constraints = restdef->constraints;
2556 coldef->is_from_type = false;
2557 columns = list_delete_nth_cell(columns, restpos);
2559 else
2560 ereport(ERROR,
2561 (errcode(ERRCODE_DUPLICATE_COLUMN),
2562 errmsg("column \"%s\" specified more than once",
2563 coldef->colname)));
2565 else
2566 restpos++;
2571 * In case of a partition, there are no new column definitions, only dummy
2572 * ColumnDefs created for column constraints. Set them aside for now and
2573 * process them at the end.
2575 if (is_partition)
2577 saved_columns = columns;
2578 columns = NIL;
2582 * Scan the parents left-to-right, and merge their attributes to form a
2583 * list of inherited columns (inh_columns).
2585 child_attno = 0;
2586 foreach(lc, supers)
2588 Oid parent = lfirst_oid(lc);
2589 Relation relation;
2590 TupleDesc tupleDesc;
2591 TupleConstr *constr;
2592 AttrMap *newattmap;
2593 List *inherited_defaults;
2594 List *cols_with_defaults;
2595 List *nnconstrs;
2596 ListCell *lc1;
2597 ListCell *lc2;
2598 Bitmapset *pkattrs;
2599 Bitmapset *nncols = NULL;
2601 /* caller already got lock */
2602 relation = table_open(parent, NoLock);
2605 * Check for active uses of the parent partitioned table in the
2606 * current transaction, such as being used in some manner by an
2607 * enclosing command.
2609 if (is_partition)
2610 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2613 * We do not allow partitioned tables and partitions to participate in
2614 * regular inheritance.
2616 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2617 ereport(ERROR,
2618 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2619 errmsg("cannot inherit from partitioned table \"%s\"",
2620 RelationGetRelationName(relation))));
2621 if (relation->rd_rel->relispartition && !is_partition)
2622 ereport(ERROR,
2623 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2624 errmsg("cannot inherit from partition \"%s\"",
2625 RelationGetRelationName(relation))));
2627 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2628 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2629 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2630 ereport(ERROR,
2631 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2632 errmsg("inherited relation \"%s\" is not a table or foreign table",
2633 RelationGetRelationName(relation))));
2636 * If the parent is permanent, so must be all of its partitions. Note
2637 * that inheritance allows that case.
2639 if (is_partition &&
2640 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2641 relpersistence == RELPERSISTENCE_TEMP)
2642 ereport(ERROR,
2643 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2644 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2645 RelationGetRelationName(relation))));
2647 /* Permanent rels cannot inherit from temporary ones */
2648 if (relpersistence != RELPERSISTENCE_TEMP &&
2649 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2650 ereport(ERROR,
2651 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2652 errmsg(!is_partition
2653 ? "cannot inherit from temporary relation \"%s\""
2654 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2655 RelationGetRelationName(relation))));
2657 /* If existing rel is temp, it must belong to this session */
2658 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2659 !relation->rd_islocaltemp)
2660 ereport(ERROR,
2661 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2662 errmsg(!is_partition
2663 ? "cannot inherit from temporary relation of another session"
2664 : "cannot create as partition of temporary relation of another session")));
2667 * We should have an UNDER permission flag for this, but for now,
2668 * demand that creator of a child table own the parent.
2670 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2671 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2672 RelationGetRelationName(relation));
2674 tupleDesc = RelationGetDescr(relation);
2675 constr = tupleDesc->constr;
2678 * newattmap->attnums[] will contain the child-table attribute numbers
2679 * for the attributes of this parent table. (They are not the same
2680 * for parents after the first one, nor if we have dropped columns.)
2682 newattmap = make_attrmap(tupleDesc->natts);
2684 /* We can't process inherited defaults until newattmap is complete. */
2685 inherited_defaults = cols_with_defaults = NIL;
2688 * All columns that are part of the parent's primary key need to be
2689 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2690 * constraint (if they don't have one already). Also, we request
2691 * attnotnull on columns that have a not-null constraint that's not
2692 * marked NO INHERIT.
2694 pkattrs = RelationGetIndexAttrBitmap(relation,
2695 INDEX_ATTR_BITMAP_PRIMARY_KEY);
2696 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
2697 foreach(lc1, nnconstrs)
2698 nncols = bms_add_member(nncols,
2699 ((CookedConstraint *) lfirst(lc1))->attnum);
2701 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2702 parent_attno++)
2704 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2705 parent_attno - 1);
2706 char *attributeName = NameStr(attribute->attname);
2707 int exist_attno;
2708 ColumnDef *newdef;
2709 ColumnDef *mergeddef;
2712 * Ignore dropped columns in the parent.
2714 if (attribute->attisdropped)
2715 continue; /* leave newattmap->attnums entry as zero */
2718 * Create new column definition
2720 newdef = makeColumnDef(attributeName, attribute->atttypid,
2721 attribute->atttypmod, attribute->attcollation);
2722 newdef->storage = attribute->attstorage;
2723 newdef->generated = attribute->attgenerated;
2724 if (CompressionMethodIsValid(attribute->attcompression))
2725 newdef->compression =
2726 pstrdup(GetCompressionMethodName(attribute->attcompression));
2729 * Regular inheritance children are independent enough not to
2730 * inherit identity columns. But partitions are integral part of
2731 * a partitioned table and inherit identity column.
2733 if (is_partition)
2734 newdef->identity = attribute->attidentity;
2737 * Does it match some previously considered column from another
2738 * parent?
2740 exist_attno = findAttrByName(attributeName, inh_columns);
2741 if (exist_attno > 0)
2744 * Yes, try to merge the two column definitions.
2746 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2748 newattmap->attnums[parent_attno - 1] = exist_attno;
2751 * Partitions have only one parent, so conflict should never
2752 * occur.
2754 Assert(!is_partition);
2756 else
2759 * No, create a new inherited column
2761 newdef->inhcount = 1;
2762 newdef->is_local = false;
2763 inh_columns = lappend(inh_columns, newdef);
2765 newattmap->attnums[parent_attno - 1] = ++child_attno;
2767 mergeddef = newdef;
2771 * mark attnotnull if parent has it and it's not NO INHERIT
2773 if (bms_is_member(parent_attno, nncols) ||
2774 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2775 pkattrs))
2776 mergeddef->is_not_null = true;
2779 * In regular inheritance, columns in the parent's primary key get
2780 * an extra not-null constraint. Partitioning doesn't need this,
2781 * because the PK itself is going to be cloned to the partition.
2783 if (!is_partition &&
2784 bms_is_member(parent_attno -
2785 FirstLowInvalidHeapAttributeNumber,
2786 pkattrs))
2788 CookedConstraint *nn;
2790 nn = palloc(sizeof(CookedConstraint));
2791 nn->contype = CONSTR_NOTNULL;
2792 nn->conoid = InvalidOid;
2793 nn->name = NULL;
2794 nn->attnum = newattmap->attnums[parent_attno - 1];
2795 nn->expr = NULL;
2796 nn->skip_validation = false;
2797 nn->is_local = false;
2798 nn->inhcount = 1;
2799 nn->is_no_inherit = false;
2801 nnconstraints = lappend(nnconstraints, nn);
2805 * Locate default/generation expression if any
2807 if (attribute->atthasdef)
2809 Node *this_default;
2811 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2812 if (this_default == NULL)
2813 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2814 parent_attno, RelationGetRelationName(relation));
2817 * If it's a GENERATED default, it might contain Vars that
2818 * need to be mapped to the inherited column(s)' new numbers.
2819 * We can't do that till newattmap is ready, so just remember
2820 * all the inherited default expressions for the moment.
2822 inherited_defaults = lappend(inherited_defaults, this_default);
2823 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2828 * Now process any inherited default expressions, adjusting attnos
2829 * using the completed newattmap map.
2831 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2833 Node *this_default = (Node *) lfirst(lc1);
2834 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2835 bool found_whole_row;
2837 /* Adjust Vars to match new table's column numbering */
2838 this_default = map_variable_attnos(this_default,
2839 1, 0,
2840 newattmap,
2841 InvalidOid, &found_whole_row);
2844 * For the moment we have to reject whole-row variables. We could
2845 * convert them, if we knew the new table's rowtype OID, but that
2846 * hasn't been assigned yet. (A variable could only appear in a
2847 * generation expression, so the error message is correct.)
2849 if (found_whole_row)
2850 ereport(ERROR,
2851 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2852 errmsg("cannot convert whole-row table reference"),
2853 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2854 def->colname,
2855 RelationGetRelationName(relation))));
2858 * If we already had a default from some prior parent, check to
2859 * see if they are the same. If so, no problem; if not, mark the
2860 * column as having a bogus default. Below, we will complain if
2861 * the bogus default isn't overridden by the child columns.
2863 Assert(def->raw_default == NULL);
2864 if (def->cooked_default == NULL)
2865 def->cooked_default = this_default;
2866 else if (!equal(def->cooked_default, this_default))
2868 def->cooked_default = &bogus_marker;
2869 have_bogus_defaults = true;
2874 * Now copy the CHECK constraints of this parent, adjusting attnos
2875 * using the completed newattmap map. Identically named constraints
2876 * are merged if possible, else we throw error.
2878 if (constr && constr->num_check > 0)
2880 ConstrCheck *check = constr->check;
2882 for (int i = 0; i < constr->num_check; i++)
2884 char *name = check[i].ccname;
2885 Node *expr;
2886 bool found_whole_row;
2888 /* ignore if the constraint is non-inheritable */
2889 if (check[i].ccnoinherit)
2890 continue;
2892 /* Adjust Vars to match new table's column numbering */
2893 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2894 1, 0,
2895 newattmap,
2896 InvalidOid, &found_whole_row);
2899 * For the moment we have to reject whole-row variables. We
2900 * could convert them, if we knew the new table's rowtype OID,
2901 * but that hasn't been assigned yet.
2903 if (found_whole_row)
2904 ereport(ERROR,
2905 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2906 errmsg("cannot convert whole-row table reference"),
2907 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2908 name,
2909 RelationGetRelationName(relation))));
2911 constraints = MergeCheckConstraint(constraints, name, expr);
2916 * Also copy the not-null constraints from this parent. The
2917 * attnotnull markings were already installed above.
2919 foreach(lc1, nnconstrs)
2921 CookedConstraint *nn = lfirst(lc1);
2923 Assert(nn->contype == CONSTR_NOTNULL);
2925 nn->attnum = newattmap->attnums[nn->attnum - 1];
2926 nn->is_local = false;
2927 nn->inhcount = 1;
2929 nnconstraints = lappend(nnconstraints, nn);
2932 free_attrmap(newattmap);
2935 * Close the parent rel, but keep our lock on it until xact commit.
2936 * That will prevent someone else from deleting or ALTERing the parent
2937 * before the child is committed.
2939 table_close(relation, NoLock);
2943 * If we had no inherited attributes, the result columns are just the
2944 * explicitly declared columns. Otherwise, we need to merge the declared
2945 * columns into the inherited column list. Although, we never have any
2946 * explicitly declared columns if the table is a partition.
2948 if (inh_columns != NIL)
2950 int newcol_attno = 0;
2952 foreach(lc, columns)
2954 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2955 char *attributeName = newdef->colname;
2956 int exist_attno;
2959 * Partitions have only one parent and have no column definitions
2960 * of their own, so conflict should never occur.
2962 Assert(!is_partition);
2964 newcol_attno++;
2967 * Does it match some inherited column?
2969 exist_attno = findAttrByName(attributeName, inh_columns);
2970 if (exist_attno > 0)
2973 * Yes, try to merge the two column definitions.
2975 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2977 else
2980 * No, attach new column unchanged to result columns.
2982 inh_columns = lappend(inh_columns, newdef);
2986 columns = inh_columns;
2989 * Check that we haven't exceeded the legal # of columns after merging
2990 * in inherited columns.
2992 if (list_length(columns) > MaxHeapAttributeNumber)
2993 ereport(ERROR,
2994 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2995 errmsg("tables can have at most %d columns",
2996 MaxHeapAttributeNumber)));
3000 * Now that we have the column definition list for a partition, we can
3001 * check whether the columns referenced in the column constraint specs
3002 * actually exist. Also, merge column defaults.
3004 if (is_partition)
3006 foreach(lc, saved_columns)
3008 ColumnDef *restdef = lfirst(lc);
3009 bool found = false;
3010 ListCell *l;
3012 foreach(l, columns)
3014 ColumnDef *coldef = lfirst(l);
3016 if (strcmp(coldef->colname, restdef->colname) == 0)
3018 found = true;
3021 * Check for conflicts related to generated columns.
3023 * Same rules as above: generated-ness has to match the
3024 * parent, but the contents of the generation expression
3025 * can be different.
3027 if (coldef->generated)
3029 if (restdef->raw_default && !restdef->generated)
3030 ereport(ERROR,
3031 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3032 errmsg("column \"%s\" inherits from generated column but specifies default",
3033 restdef->colname)));
3034 if (restdef->identity)
3035 ereport(ERROR,
3036 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3037 errmsg("column \"%s\" inherits from generated column but specifies identity",
3038 restdef->colname)));
3040 else
3042 if (restdef->generated)
3043 ereport(ERROR,
3044 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3045 errmsg("child column \"%s\" specifies generation expression",
3046 restdef->colname),
3047 errhint("A child table column cannot be generated unless its parent column is.")));
3051 * Override the parent's default value for this column
3052 * (coldef->cooked_default) with the partition's local
3053 * definition (restdef->raw_default), if there's one. It
3054 * should be physically impossible to get a cooked default
3055 * in the local definition or a raw default in the
3056 * inherited definition, but make sure they're nulls, for
3057 * future-proofing.
3059 Assert(restdef->cooked_default == NULL);
3060 Assert(coldef->raw_default == NULL);
3061 if (restdef->raw_default)
3063 coldef->raw_default = restdef->raw_default;
3064 coldef->cooked_default = NULL;
3069 /* complain for constraints on columns not in parent */
3070 if (!found)
3071 ereport(ERROR,
3072 (errcode(ERRCODE_UNDEFINED_COLUMN),
3073 errmsg("column \"%s\" does not exist",
3074 restdef->colname)));
3079 * If we found any conflicting parent default values, check to make sure
3080 * they were overridden by the child.
3082 if (have_bogus_defaults)
3084 foreach(lc, columns)
3086 ColumnDef *def = lfirst(lc);
3088 if (def->cooked_default == &bogus_marker)
3090 if (def->generated)
3091 ereport(ERROR,
3092 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3093 errmsg("column \"%s\" inherits conflicting generation expressions",
3094 def->colname),
3095 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3096 else
3097 ereport(ERROR,
3098 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3099 errmsg("column \"%s\" inherits conflicting default values",
3100 def->colname),
3101 errhint("To resolve the conflict, specify a default explicitly.")));
3106 *supconstr = constraints;
3107 *supnotnulls = nnconstraints;
3109 return columns;
3114 * MergeCheckConstraint
3115 * Try to merge an inherited CHECK constraint with previous ones
3117 * If we inherit identically-named constraints from multiple parents, we must
3118 * merge them, or throw an error if they don't have identical definitions.
3120 * constraints is a list of CookedConstraint structs for previous constraints.
3122 * If the new constraint matches an existing one, then the existing
3123 * constraint's inheritance count is updated. If there is a conflict (same
3124 * name but different expression), throw an error. If the constraint neither
3125 * matches nor conflicts with an existing one, a new constraint is appended to
3126 * the list.
3128 static List *
3129 MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3131 ListCell *lc;
3132 CookedConstraint *newcon;
3134 foreach(lc, constraints)
3136 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3138 Assert(ccon->contype == CONSTR_CHECK);
3140 /* Non-matching names never conflict */
3141 if (strcmp(ccon->name, name) != 0)
3142 continue;
3144 if (equal(expr, ccon->expr))
3146 /* OK to merge constraint with existing */
3147 ccon->inhcount++;
3148 if (ccon->inhcount < 0)
3149 ereport(ERROR,
3150 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3151 errmsg("too many inheritance parents"));
3152 return constraints;
3155 ereport(ERROR,
3156 (errcode(ERRCODE_DUPLICATE_OBJECT),
3157 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3158 name)));
3162 * Constraint couldn't be merged with an existing one and also didn't
3163 * conflict with an existing one, so add it as a new one to the list.
3165 newcon = palloc0_object(CookedConstraint);
3166 newcon->contype = CONSTR_CHECK;
3167 newcon->name = pstrdup(name);
3168 newcon->expr = expr;
3169 newcon->inhcount = 1;
3170 return lappend(constraints, newcon);
3174 * MergeChildAttribute
3175 * Merge given child attribute definition into given inherited attribute.
3177 * Input arguments:
3178 * 'inh_columns' is the list of inherited ColumnDefs.
3179 * 'exist_attno' is the number of the inherited attribute in inh_columns
3180 * 'newcol_attno' is the attribute number in child table's schema definition
3181 * 'newdef' is the column/attribute definition from the child table.
3183 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3184 * ColumnDef remains unchanged.
3186 * Notes:
3187 * - The attribute is merged according to the rules laid out in the prologue
3188 * of MergeAttributes().
3189 * - If matching inherited attribute exists but the child attribute can not be
3190 * merged into it, the function throws respective errors.
3191 * - A partition can not have its own column definitions. Hence this function
3192 * is applicable only to a regular inheritance child.
3194 static void
3195 MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3197 char *attributeName = newdef->colname;
3198 ColumnDef *inhdef;
3199 Oid inhtypeid,
3200 newtypeid;
3201 int32 inhtypmod,
3202 newtypmod;
3203 Oid inhcollid,
3204 newcollid;
3206 if (exist_attno == newcol_attno)
3207 ereport(NOTICE,
3208 (errmsg("merging column \"%s\" with inherited definition",
3209 attributeName)));
3210 else
3211 ereport(NOTICE,
3212 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3213 errdetail("User-specified column moved to the position of the inherited column.")));
3215 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3218 * Must have the same type and typmod
3220 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3221 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3222 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3223 ereport(ERROR,
3224 (errcode(ERRCODE_DATATYPE_MISMATCH),
3225 errmsg("column \"%s\" has a type conflict",
3226 attributeName),
3227 errdetail("%s versus %s",
3228 format_type_with_typemod(inhtypeid, inhtypmod),
3229 format_type_with_typemod(newtypeid, newtypmod))));
3232 * Must have the same collation
3234 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3235 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3236 if (inhcollid != newcollid)
3237 ereport(ERROR,
3238 (errcode(ERRCODE_COLLATION_MISMATCH),
3239 errmsg("column \"%s\" has a collation conflict",
3240 attributeName),
3241 errdetail("\"%s\" versus \"%s\"",
3242 get_collation_name(inhcollid),
3243 get_collation_name(newcollid))));
3246 * Identity is never inherited by a regular inheritance child. Pick
3247 * child's identity definition if there's one.
3249 inhdef->identity = newdef->identity;
3252 * Copy storage parameter
3254 if (inhdef->storage == 0)
3255 inhdef->storage = newdef->storage;
3256 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3257 ereport(ERROR,
3258 (errcode(ERRCODE_DATATYPE_MISMATCH),
3259 errmsg("column \"%s\" has a storage parameter conflict",
3260 attributeName),
3261 errdetail("%s versus %s",
3262 storage_name(inhdef->storage),
3263 storage_name(newdef->storage))));
3266 * Copy compression parameter
3268 if (inhdef->compression == NULL)
3269 inhdef->compression = newdef->compression;
3270 else if (newdef->compression != NULL)
3272 if (strcmp(inhdef->compression, newdef->compression) != 0)
3273 ereport(ERROR,
3274 (errcode(ERRCODE_DATATYPE_MISMATCH),
3275 errmsg("column \"%s\" has a compression method conflict",
3276 attributeName),
3277 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3281 * Merge of not-null constraints = OR 'em together
3283 inhdef->is_not_null |= newdef->is_not_null;
3286 * Check for conflicts related to generated columns.
3288 * If the parent column is generated, the child column will be made a
3289 * generated column if it isn't already. If it is a generated column,
3290 * we'll take its generation expression in preference to the parent's. We
3291 * must check that the child column doesn't specify a default value or
3292 * identity, which matches the rules for a single column in
3293 * parse_utilcmd.c.
3295 * Conversely, if the parent column is not generated, the child column
3296 * can't be either. (We used to allow that, but it results in being able
3297 * to override the generation expression via UPDATEs through the parent.)
3299 if (inhdef->generated)
3301 if (newdef->raw_default && !newdef->generated)
3302 ereport(ERROR,
3303 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3304 errmsg("column \"%s\" inherits from generated column but specifies default",
3305 inhdef->colname)));
3306 if (newdef->identity)
3307 ereport(ERROR,
3308 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3309 errmsg("column \"%s\" inherits from generated column but specifies identity",
3310 inhdef->colname)));
3312 else
3314 if (newdef->generated)
3315 ereport(ERROR,
3316 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3317 errmsg("child column \"%s\" specifies generation expression",
3318 inhdef->colname),
3319 errhint("A child table column cannot be generated unless its parent column is.")));
3323 * If new def has a default, override previous default
3325 if (newdef->raw_default != NULL)
3327 inhdef->raw_default = newdef->raw_default;
3328 inhdef->cooked_default = newdef->cooked_default;
3331 /* Mark the column as locally defined */
3332 inhdef->is_local = true;
3336 * MergeInheritedAttribute
3337 * Merge given parent attribute definition into specified attribute
3338 * inherited from the previous parents.
3340 * Input arguments:
3341 * 'inh_columns' is the list of previously inherited ColumnDefs.
3342 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3343 * 'newdef' is the new parent column/attribute definition to be merged.
3345 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3347 * Notes:
3348 * - The attribute is merged according to the rules laid out in the prologue
3349 * of MergeAttributes().
3350 * - If matching inherited attribute exists but the new attribute can not be
3351 * merged into it, the function throws respective errors.
3352 * - A partition inherits from only a single parent. Hence this function is
3353 * applicable only to a regular inheritance.
3355 static ColumnDef *
3356 MergeInheritedAttribute(List *inh_columns,
3357 int exist_attno,
3358 const ColumnDef *newdef)
3360 char *attributeName = newdef->colname;
3361 ColumnDef *prevdef;
3362 Oid prevtypeid,
3363 newtypeid;
3364 int32 prevtypmod,
3365 newtypmod;
3366 Oid prevcollid,
3367 newcollid;
3369 ereport(NOTICE,
3370 (errmsg("merging multiple inherited definitions of column \"%s\"",
3371 attributeName)));
3372 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3375 * Must have the same type and typmod
3377 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3378 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3379 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3380 ereport(ERROR,
3381 (errcode(ERRCODE_DATATYPE_MISMATCH),
3382 errmsg("inherited column \"%s\" has a type conflict",
3383 attributeName),
3384 errdetail("%s versus %s",
3385 format_type_with_typemod(prevtypeid, prevtypmod),
3386 format_type_with_typemod(newtypeid, newtypmod))));
3389 * Must have the same collation
3391 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3392 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3393 if (prevcollid != newcollid)
3394 ereport(ERROR,
3395 (errcode(ERRCODE_COLLATION_MISMATCH),
3396 errmsg("inherited column \"%s\" has a collation conflict",
3397 attributeName),
3398 errdetail("\"%s\" versus \"%s\"",
3399 get_collation_name(prevcollid),
3400 get_collation_name(newcollid))));
3403 * Copy/check storage parameter
3405 if (prevdef->storage == 0)
3406 prevdef->storage = newdef->storage;
3407 else if (prevdef->storage != newdef->storage)
3408 ereport(ERROR,
3409 (errcode(ERRCODE_DATATYPE_MISMATCH),
3410 errmsg("inherited column \"%s\" has a storage parameter conflict",
3411 attributeName),
3412 errdetail("%s versus %s",
3413 storage_name(prevdef->storage),
3414 storage_name(newdef->storage))));
3417 * Copy/check compression parameter
3419 if (prevdef->compression == NULL)
3420 prevdef->compression = newdef->compression;
3421 else if (strcmp(prevdef->compression, newdef->compression) != 0)
3422 ereport(ERROR,
3423 (errcode(ERRCODE_DATATYPE_MISMATCH),
3424 errmsg("column \"%s\" has a compression method conflict",
3425 attributeName),
3426 errdetail("%s versus %s", prevdef->compression, newdef->compression)));
3429 * Check for GENERATED conflicts
3431 if (prevdef->generated != newdef->generated)
3432 ereport(ERROR,
3433 (errcode(ERRCODE_DATATYPE_MISMATCH),
3434 errmsg("inherited column \"%s\" has a generation conflict",
3435 attributeName)));
3438 * Default and other constraints are handled by the caller.
3441 prevdef->inhcount++;
3442 if (prevdef->inhcount < 0)
3443 ereport(ERROR,
3444 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3445 errmsg("too many inheritance parents"));
3447 return prevdef;
3451 * StoreCatalogInheritance
3452 * Updates the system catalogs with proper inheritance information.
3454 * supers is a list of the OIDs of the new relation's direct ancestors.
3456 static void
3457 StoreCatalogInheritance(Oid relationId, List *supers,
3458 bool child_is_partition)
3460 Relation relation;
3461 int32 seqNumber;
3462 ListCell *entry;
3465 * sanity checks
3467 Assert(OidIsValid(relationId));
3469 if (supers == NIL)
3470 return;
3473 * Store INHERITS information in pg_inherits using direct ancestors only.
3474 * Also enter dependencies on the direct ancestors, and make sure they are
3475 * marked with relhassubclass = true.
3477 * (Once upon a time, both direct and indirect ancestors were found here
3478 * and then entered into pg_ipl. Since that catalog doesn't exist
3479 * anymore, there's no need to look for indirect ancestors.)
3481 relation = table_open(InheritsRelationId, RowExclusiveLock);
3483 seqNumber = 1;
3484 foreach(entry, supers)
3486 Oid parentOid = lfirst_oid(entry);
3488 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3489 child_is_partition);
3490 seqNumber++;
3493 table_close(relation, RowExclusiveLock);
3497 * Make catalog entries showing relationId as being an inheritance child
3498 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3500 static void
3501 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3502 int32 seqNumber, Relation inhRelation,
3503 bool child_is_partition)
3505 ObjectAddress childobject,
3506 parentobject;
3508 /* store the pg_inherits row */
3509 StoreSingleInheritance(relationId, parentOid, seqNumber);
3512 * Store a dependency too
3514 parentobject.classId = RelationRelationId;
3515 parentobject.objectId = parentOid;
3516 parentobject.objectSubId = 0;
3517 childobject.classId = RelationRelationId;
3518 childobject.objectId = relationId;
3519 childobject.objectSubId = 0;
3521 recordDependencyOn(&childobject, &parentobject,
3522 child_dependency_type(child_is_partition));
3525 * Post creation hook of this inheritance. Since object_access_hook
3526 * doesn't take multiple object identifiers, we relay oid of parent
3527 * relation using auxiliary_id argument.
3529 InvokeObjectPostAlterHookArg(InheritsRelationId,
3530 relationId, 0,
3531 parentOid, false);
3534 * Mark the parent as having subclasses.
3536 SetRelationHasSubclass(parentOid, true);
3540 * Look for an existing column entry with the given name.
3542 * Returns the index (starting with 1) if attribute already exists in columns,
3543 * 0 if it doesn't.
3545 static int
3546 findAttrByName(const char *attributeName, const List *columns)
3548 ListCell *lc;
3549 int i = 1;
3551 foreach(lc, columns)
3553 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3554 return i;
3556 i++;
3558 return 0;
3563 * SetRelationHasSubclass
3564 * Set the value of the relation's relhassubclass field in pg_class.
3566 * NOTE: caller must be holding an appropriate lock on the relation.
3567 * ShareUpdateExclusiveLock is sufficient.
3569 * NOTE: an important side-effect of this operation is that an SI invalidation
3570 * message is sent out to all backends --- including me --- causing plans
3571 * referencing the relation to be rebuilt with the new list of children.
3572 * This must happen even if we find that no change is needed in the pg_class
3573 * row.
3575 void
3576 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3578 Relation relationRelation;
3579 HeapTuple tuple;
3580 Form_pg_class classtuple;
3583 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3585 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3586 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3587 if (!HeapTupleIsValid(tuple))
3588 elog(ERROR, "cache lookup failed for relation %u", relationId);
3589 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3591 if (classtuple->relhassubclass != relhassubclass)
3593 classtuple->relhassubclass = relhassubclass;
3594 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3596 else
3598 /* no need to change tuple, but force relcache rebuild anyway */
3599 CacheInvalidateRelcacheByTuple(tuple);
3602 heap_freetuple(tuple);
3603 table_close(relationRelation, RowExclusiveLock);
3607 * CheckRelationTableSpaceMove
3608 * Check if relation can be moved to new tablespace.
3610 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3612 * Returns true if the relation can be moved to the new tablespace; raises
3613 * an error if it is not possible to do the move; returns false if the move
3614 * would have no effect.
3616 bool
3617 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3619 Oid oldTableSpaceId;
3622 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3623 * stored as 0.
3625 oldTableSpaceId = rel->rd_rel->reltablespace;
3626 if (newTableSpaceId == oldTableSpaceId ||
3627 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3628 return false;
3631 * We cannot support moving mapped relations into different tablespaces.
3632 * (In particular this eliminates all shared catalogs.)
3634 if (RelationIsMapped(rel))
3635 ereport(ERROR,
3636 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3637 errmsg("cannot move system relation \"%s\"",
3638 RelationGetRelationName(rel))));
3640 /* Cannot move a non-shared relation into pg_global */
3641 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3642 ereport(ERROR,
3643 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3644 errmsg("only shared relations can be placed in pg_global tablespace")));
3647 * Do not allow moving temp tables of other backends ... their local
3648 * buffer manager is not going to cope.
3650 if (RELATION_IS_OTHER_TEMP(rel))
3651 ereport(ERROR,
3652 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3653 errmsg("cannot move temporary tables of other sessions")));
3655 return true;
3659 * SetRelationTableSpace
3660 * Set new reltablespace and relfilenumber in pg_class entry.
3662 * newTableSpaceId is the new tablespace for the relation, and
3663 * newRelFilenumber its new filenumber. If newRelFilenumber is
3664 * InvalidRelFileNumber, this field is not updated.
3666 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3668 * The caller of this routine had better check if a relation can be
3669 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3670 * first, and is responsible for making the change visible with
3671 * CommandCounterIncrement().
3673 void
3674 SetRelationTableSpace(Relation rel,
3675 Oid newTableSpaceId,
3676 RelFileNumber newRelFilenumber)
3678 Relation pg_class;
3679 HeapTuple tuple;
3680 Form_pg_class rd_rel;
3681 Oid reloid = RelationGetRelid(rel);
3683 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3685 /* Get a modifiable copy of the relation's pg_class row. */
3686 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3688 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3689 if (!HeapTupleIsValid(tuple))
3690 elog(ERROR, "cache lookup failed for relation %u", reloid);
3691 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3693 /* Update the pg_class row. */
3694 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3695 InvalidOid : newTableSpaceId;
3696 if (RelFileNumberIsValid(newRelFilenumber))
3697 rd_rel->relfilenode = newRelFilenumber;
3698 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3701 * Record dependency on tablespace. This is only required for relations
3702 * that have no physical storage.
3704 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3705 changeDependencyOnTablespace(RelationRelationId, reloid,
3706 rd_rel->reltablespace);
3708 heap_freetuple(tuple);
3709 table_close(pg_class, RowExclusiveLock);
3713 * renameatt_check - basic sanity checks before attribute rename
3715 static void
3716 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3718 char relkind = classform->relkind;
3720 if (classform->reloftype && !recursing)
3721 ereport(ERROR,
3722 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3723 errmsg("cannot rename column of typed table")));
3726 * Renaming the columns of sequences or toast tables doesn't actually
3727 * break anything from the system's point of view, since internal
3728 * references are by attnum. But it doesn't seem right to allow users to
3729 * change names that are hardcoded into the system, hence the following
3730 * restriction.
3732 if (relkind != RELKIND_RELATION &&
3733 relkind != RELKIND_VIEW &&
3734 relkind != RELKIND_MATVIEW &&
3735 relkind != RELKIND_COMPOSITE_TYPE &&
3736 relkind != RELKIND_INDEX &&
3737 relkind != RELKIND_PARTITIONED_INDEX &&
3738 relkind != RELKIND_FOREIGN_TABLE &&
3739 relkind != RELKIND_PARTITIONED_TABLE)
3740 ereport(ERROR,
3741 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3742 errmsg("cannot rename columns of relation \"%s\"",
3743 NameStr(classform->relname)),
3744 errdetail_relkind_not_supported(relkind)));
3747 * permissions checking. only the owner of a class can change its schema.
3749 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3750 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3751 NameStr(classform->relname));
3752 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3753 ereport(ERROR,
3754 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3755 errmsg("permission denied: \"%s\" is a system catalog",
3756 NameStr(classform->relname))));
3760 * renameatt_internal - workhorse for renameatt
3762 * Return value is the attribute number in the 'myrelid' relation.
3764 static AttrNumber
3765 renameatt_internal(Oid myrelid,
3766 const char *oldattname,
3767 const char *newattname,
3768 bool recurse,
3769 bool recursing,
3770 int expected_parents,
3771 DropBehavior behavior)
3773 Relation targetrelation;
3774 Relation attrelation;
3775 HeapTuple atttup;
3776 Form_pg_attribute attform;
3777 AttrNumber attnum;
3780 * Grab an exclusive lock on the target table, which we will NOT release
3781 * until end of transaction.
3783 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3784 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3787 * if the 'recurse' flag is set then we are supposed to rename this
3788 * attribute in all classes that inherit from 'relname' (as well as in
3789 * 'relname').
3791 * any permissions or problems with duplicate attributes will cause the
3792 * whole transaction to abort, which is what we want -- all or nothing.
3794 if (recurse)
3796 List *child_oids,
3797 *child_numparents;
3798 ListCell *lo,
3799 *li;
3802 * we need the number of parents for each child so that the recursive
3803 * calls to renameatt() can determine whether there are any parents
3804 * outside the inheritance hierarchy being processed.
3806 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3807 &child_numparents);
3810 * find_all_inheritors does the recursive search of the inheritance
3811 * hierarchy, so all we have to do is process all of the relids in the
3812 * list that it returns.
3814 forboth(lo, child_oids, li, child_numparents)
3816 Oid childrelid = lfirst_oid(lo);
3817 int numparents = lfirst_int(li);
3819 if (childrelid == myrelid)
3820 continue;
3821 /* note we need not recurse again */
3822 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3825 else
3828 * If we are told not to recurse, there had better not be any child
3829 * tables; else the rename would put them out of step.
3831 * expected_parents will only be 0 if we are not already recursing.
3833 if (expected_parents == 0 &&
3834 find_inheritance_children(myrelid, NoLock) != NIL)
3835 ereport(ERROR,
3836 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3837 errmsg("inherited column \"%s\" must be renamed in child tables too",
3838 oldattname)));
3841 /* rename attributes in typed tables of composite type */
3842 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3844 List *child_oids;
3845 ListCell *lo;
3847 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3848 RelationGetRelationName(targetrelation),
3849 behavior);
3851 foreach(lo, child_oids)
3852 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3855 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3857 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3858 if (!HeapTupleIsValid(atttup))
3859 ereport(ERROR,
3860 (errcode(ERRCODE_UNDEFINED_COLUMN),
3861 errmsg("column \"%s\" does not exist",
3862 oldattname)));
3863 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3865 attnum = attform->attnum;
3866 if (attnum <= 0)
3867 ereport(ERROR,
3868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3869 errmsg("cannot rename system column \"%s\"",
3870 oldattname)));
3873 * if the attribute is inherited, forbid the renaming. if this is a
3874 * top-level call to renameatt(), then expected_parents will be 0, so the
3875 * effect of this code will be to prohibit the renaming if the attribute
3876 * is inherited at all. if this is a recursive call to renameatt(),
3877 * expected_parents will be the number of parents the current relation has
3878 * within the inheritance hierarchy being processed, so we'll prohibit the
3879 * renaming only if there are additional parents from elsewhere.
3881 if (attform->attinhcount > expected_parents)
3882 ereport(ERROR,
3883 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3884 errmsg("cannot rename inherited column \"%s\"",
3885 oldattname)));
3887 /* new name should not already exist */
3888 (void) check_for_column_name_collision(targetrelation, newattname, false);
3890 /* apply the update */
3891 namestrcpy(&(attform->attname), newattname);
3893 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3895 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3897 heap_freetuple(atttup);
3899 table_close(attrelation, RowExclusiveLock);
3901 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3903 return attnum;
3907 * Perform permissions and integrity checks before acquiring a relation lock.
3909 static void
3910 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3911 void *arg)
3913 HeapTuple tuple;
3914 Form_pg_class form;
3916 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3917 if (!HeapTupleIsValid(tuple))
3918 return; /* concurrently dropped */
3919 form = (Form_pg_class) GETSTRUCT(tuple);
3920 renameatt_check(relid, form, false);
3921 ReleaseSysCache(tuple);
3925 * renameatt - changes the name of an attribute in a relation
3927 * The returned ObjectAddress is that of the renamed column.
3929 ObjectAddress
3930 renameatt(RenameStmt *stmt)
3932 Oid relid;
3933 AttrNumber attnum;
3934 ObjectAddress address;
3936 /* lock level taken here should match renameatt_internal */
3937 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3938 stmt->missing_ok ? RVR_MISSING_OK : 0,
3939 RangeVarCallbackForRenameAttribute,
3940 NULL);
3942 if (!OidIsValid(relid))
3944 ereport(NOTICE,
3945 (errmsg("relation \"%s\" does not exist, skipping",
3946 stmt->relation->relname)));
3947 return InvalidObjectAddress;
3950 attnum =
3951 renameatt_internal(relid,
3952 stmt->subname, /* old att name */
3953 stmt->newname, /* new att name */
3954 stmt->relation->inh, /* recursive? */
3955 false, /* recursing? */
3956 0, /* expected inhcount */
3957 stmt->behavior);
3959 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3961 return address;
3965 * same logic as renameatt_internal
3967 static ObjectAddress
3968 rename_constraint_internal(Oid myrelid,
3969 Oid mytypid,
3970 const char *oldconname,
3971 const char *newconname,
3972 bool recurse,
3973 bool recursing,
3974 int expected_parents)
3976 Relation targetrelation = NULL;
3977 Oid constraintOid;
3978 HeapTuple tuple;
3979 Form_pg_constraint con;
3980 ObjectAddress address;
3982 Assert(!myrelid || !mytypid);
3984 if (mytypid)
3986 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3988 else
3990 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3993 * don't tell it whether we're recursing; we allow changing typed
3994 * tables here
3996 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3998 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4001 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4002 if (!HeapTupleIsValid(tuple))
4003 elog(ERROR, "cache lookup failed for constraint %u",
4004 constraintOid);
4005 con = (Form_pg_constraint) GETSTRUCT(tuple);
4007 if (myrelid &&
4008 (con->contype == CONSTRAINT_CHECK ||
4009 con->contype == CONSTRAINT_NOTNULL) &&
4010 !con->connoinherit)
4012 if (recurse)
4014 List *child_oids,
4015 *child_numparents;
4016 ListCell *lo,
4017 *li;
4019 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4020 &child_numparents);
4022 forboth(lo, child_oids, li, child_numparents)
4024 Oid childrelid = lfirst_oid(lo);
4025 int numparents = lfirst_int(li);
4027 if (childrelid == myrelid)
4028 continue;
4030 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4033 else
4035 if (expected_parents == 0 &&
4036 find_inheritance_children(myrelid, NoLock) != NIL)
4037 ereport(ERROR,
4038 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4039 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4040 oldconname)));
4043 if (con->coninhcount > expected_parents)
4044 ereport(ERROR,
4045 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4046 errmsg("cannot rename inherited constraint \"%s\"",
4047 oldconname)));
4050 if (con->conindid
4051 && (con->contype == CONSTRAINT_PRIMARY
4052 || con->contype == CONSTRAINT_UNIQUE
4053 || con->contype == CONSTRAINT_EXCLUSION))
4054 /* rename the index; this renames the constraint as well */
4055 RenameRelationInternal(con->conindid, newconname, false, true);
4056 else
4057 RenameConstraintById(constraintOid, newconname);
4059 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4061 ReleaseSysCache(tuple);
4063 if (targetrelation)
4066 * Invalidate relcache so as others can see the new constraint name.
4068 CacheInvalidateRelcache(targetrelation);
4070 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4073 return address;
4076 ObjectAddress
4077 RenameConstraint(RenameStmt *stmt)
4079 Oid relid = InvalidOid;
4080 Oid typid = InvalidOid;
4082 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4084 Relation rel;
4085 HeapTuple tup;
4087 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4088 rel = table_open(TypeRelationId, RowExclusiveLock);
4089 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4090 if (!HeapTupleIsValid(tup))
4091 elog(ERROR, "cache lookup failed for type %u", typid);
4092 checkDomainOwner(tup);
4093 ReleaseSysCache(tup);
4094 table_close(rel, NoLock);
4096 else
4098 /* lock level taken here should match rename_constraint_internal */
4099 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4100 stmt->missing_ok ? RVR_MISSING_OK : 0,
4101 RangeVarCallbackForRenameAttribute,
4102 NULL);
4103 if (!OidIsValid(relid))
4105 ereport(NOTICE,
4106 (errmsg("relation \"%s\" does not exist, skipping",
4107 stmt->relation->relname)));
4108 return InvalidObjectAddress;
4112 return
4113 rename_constraint_internal(relid, typid,
4114 stmt->subname,
4115 stmt->newname,
4116 (stmt->relation &&
4117 stmt->relation->inh), /* recursive? */
4118 false, /* recursing? */
4119 0 /* expected inhcount */ );
4123 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4124 * RENAME
4126 ObjectAddress
4127 RenameRelation(RenameStmt *stmt)
4129 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4130 Oid relid;
4131 ObjectAddress address;
4134 * Grab an exclusive lock on the target table, index, sequence, view,
4135 * materialized view, or foreign table, which we will NOT release until
4136 * end of transaction.
4138 * Lock level used here should match RenameRelationInternal, to avoid lock
4139 * escalation. However, because ALTER INDEX can be used with any relation
4140 * type, we mustn't believe without verification.
4142 for (;;)
4144 LOCKMODE lockmode;
4145 char relkind;
4146 bool obj_is_index;
4148 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4150 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4151 stmt->missing_ok ? RVR_MISSING_OK : 0,
4152 RangeVarCallbackForAlterRelation,
4153 (void *) stmt);
4155 if (!OidIsValid(relid))
4157 ereport(NOTICE,
4158 (errmsg("relation \"%s\" does not exist, skipping",
4159 stmt->relation->relname)));
4160 return InvalidObjectAddress;
4164 * We allow mismatched statement and object types (e.g., ALTER INDEX
4165 * to rename a table), but we might've used the wrong lock level. If
4166 * that happens, retry with the correct lock level. We don't bother
4167 * if we already acquired AccessExclusiveLock with an index, however.
4169 relkind = get_rel_relkind(relid);
4170 obj_is_index = (relkind == RELKIND_INDEX ||
4171 relkind == RELKIND_PARTITIONED_INDEX);
4172 if (obj_is_index || is_index_stmt == obj_is_index)
4173 break;
4175 UnlockRelationOid(relid, lockmode);
4176 is_index_stmt = obj_is_index;
4179 /* Do the work */
4180 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4182 ObjectAddressSet(address, RelationRelationId, relid);
4184 return address;
4188 * RenameRelationInternal - change the name of a relation
4190 void
4191 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4193 Relation targetrelation;
4194 Relation relrelation; /* for RELATION relation */
4195 HeapTuple reltup;
4196 Form_pg_class relform;
4197 Oid namespaceId;
4200 * Grab a lock on the target relation, which we will NOT release until end
4201 * of transaction. We need at least a self-exclusive lock so that
4202 * concurrent DDL doesn't overwrite the rename if they start updating
4203 * while still seeing the old version. The lock also guards against
4204 * triggering relcache reloads in concurrent sessions, which might not
4205 * handle this information changing under them. For indexes, we can use a
4206 * reduced lock level because RelationReloadIndexInfo() handles indexes
4207 * specially.
4209 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4210 namespaceId = RelationGetNamespace(targetrelation);
4213 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4215 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4217 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4218 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4219 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4220 relform = (Form_pg_class) GETSTRUCT(reltup);
4222 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4223 ereport(ERROR,
4224 (errcode(ERRCODE_DUPLICATE_TABLE),
4225 errmsg("relation \"%s\" already exists",
4226 newrelname)));
4229 * RenameRelation is careful not to believe the caller's idea of the
4230 * relation kind being handled. We don't have to worry about this, but
4231 * let's not be totally oblivious to it. We can process an index as
4232 * not-an-index, but not the other way around.
4234 Assert(!is_index ||
4235 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4236 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4239 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4240 * because it's a copy...)
4242 namestrcpy(&(relform->relname), newrelname);
4244 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4246 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4247 InvalidOid, is_internal);
4249 heap_freetuple(reltup);
4250 table_close(relrelation, RowExclusiveLock);
4253 * Also rename the associated type, if any.
4255 if (OidIsValid(targetrelation->rd_rel->reltype))
4256 RenameTypeInternal(targetrelation->rd_rel->reltype,
4257 newrelname, namespaceId);
4260 * Also rename the associated constraint, if any.
4262 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4263 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4265 Oid constraintId = get_index_constraint(myrelid);
4267 if (OidIsValid(constraintId))
4268 RenameConstraintById(constraintId, newrelname);
4272 * Close rel, but keep lock!
4274 relation_close(targetrelation, NoLock);
4278 * ResetRelRewrite - reset relrewrite
4280 void
4281 ResetRelRewrite(Oid myrelid)
4283 Relation relrelation; /* for RELATION relation */
4284 HeapTuple reltup;
4285 Form_pg_class relform;
4288 * Find relation's pg_class tuple.
4290 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4292 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4293 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4294 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4295 relform = (Form_pg_class) GETSTRUCT(reltup);
4298 * Update pg_class tuple.
4300 relform->relrewrite = InvalidOid;
4302 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4304 heap_freetuple(reltup);
4305 table_close(relrelation, RowExclusiveLock);
4309 * Disallow ALTER TABLE (and similar commands) when the current backend has
4310 * any open reference to the target table besides the one just acquired by
4311 * the calling command; this implies there's an open cursor or active plan.
4312 * We need this check because our lock doesn't protect us against stomping
4313 * on our own foot, only other people's feet!
4315 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4316 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4317 * possibly be relaxed to only error out for certain types of alterations.
4318 * But the use-case for allowing any of these things is not obvious, so we
4319 * won't work hard at it for now.
4321 * We also reject these commands if there are any pending AFTER trigger events
4322 * for the rel. This is certainly necessary for the rewriting variants of
4323 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4324 * events would try to fetch the wrong tuples. It might be overly cautious
4325 * in other cases, but again it seems better to err on the side of paranoia.
4327 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4328 * we are worried about active indexscans on the index. The trigger-event
4329 * check can be skipped, since we are doing no damage to the parent table.
4331 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4333 void
4334 CheckTableNotInUse(Relation rel, const char *stmt)
4336 int expected_refcnt;
4338 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4339 if (rel->rd_refcnt != expected_refcnt)
4340 ereport(ERROR,
4341 (errcode(ERRCODE_OBJECT_IN_USE),
4342 /* translator: first %s is a SQL command, eg ALTER TABLE */
4343 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4344 stmt, RelationGetRelationName(rel))));
4346 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4347 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4348 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4349 ereport(ERROR,
4350 (errcode(ERRCODE_OBJECT_IN_USE),
4351 /* translator: first %s is a SQL command, eg ALTER TABLE */
4352 errmsg("cannot %s \"%s\" because it has pending trigger events",
4353 stmt, RelationGetRelationName(rel))));
4357 * AlterTableLookupRelation
4358 * Look up, and lock, the OID for the relation named by an alter table
4359 * statement.
4362 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4364 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4365 stmt->missing_ok ? RVR_MISSING_OK : 0,
4366 RangeVarCallbackForAlterRelation,
4367 (void *) stmt);
4371 * AlterTable
4372 * Execute ALTER TABLE, which can be a list of subcommands
4374 * ALTER TABLE is performed in three phases:
4375 * 1. Examine subcommands and perform pre-transformation checking.
4376 * 2. Validate and transform subcommands, and update system catalogs.
4377 * 3. Scan table(s) to check new constraints, and optionally recopy
4378 * the data into new table(s).
4379 * Phase 3 is not performed unless one or more of the subcommands requires
4380 * it. The intention of this design is to allow multiple independent
4381 * updates of the table schema to be performed with only one pass over the
4382 * data.
4384 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4385 * each table to be affected (there may be multiple affected tables if the
4386 * commands traverse a table inheritance hierarchy). Also we do preliminary
4387 * validation of the subcommands. Because earlier subcommands may change
4388 * the catalog state seen by later commands, there are limits to what can
4389 * be done in this phase. Generally, this phase acquires table locks,
4390 * checks permissions and relkind, and recurses to find child tables.
4392 * ATRewriteCatalogs performs phase 2 for each affected table.
4393 * Certain subcommands need to be performed before others to avoid
4394 * unnecessary conflicts; for example, DROP COLUMN should come before
4395 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4396 * lists, one for each logical "pass" of phase 2.
4398 * ATRewriteTables performs phase 3 for those tables that need it.
4400 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4401 * since phase 1 already does it. However, for certain subcommand types
4402 * it is only possible to determine how to recurse at phase 2 time; for
4403 * those cases, phase 1 sets the cmd->recurse flag.
4405 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4406 * the whole operation; we don't have to do anything special to clean up.
4408 * The caller must lock the relation, with an appropriate lock level
4409 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4410 * or higher. We pass the lock level down
4411 * so that we can apply it recursively to inherited tables. Note that the
4412 * lock level we want as we recurse might well be higher than required for
4413 * that specific subcommand. So we pass down the overall lock requirement,
4414 * rather than reassess it at lower levels.
4416 * The caller also provides a "context" which is to be passed back to
4417 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4418 * Some of the fields therein, such as the relid, are used here as well.
4420 void
4421 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4422 AlterTableUtilityContext *context)
4424 Relation rel;
4426 /* Caller is required to provide an adequate lock. */
4427 rel = relation_open(context->relid, NoLock);
4429 CheckTableNotInUse(rel, "ALTER TABLE");
4431 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4435 * AlterTableInternal
4437 * ALTER TABLE with target specified by OID
4439 * We do not reject if the relation is already open, because it's quite
4440 * likely that one or more layers of caller have it open. That means it
4441 * is unsafe to use this entry point for alterations that could break
4442 * existing query plans. On the assumption it's not used for such, we
4443 * don't have to reject pending AFTER triggers, either.
4445 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4446 * used for any subcommand types that require parse transformation or
4447 * could generate subcommands that have to be passed to ProcessUtility.
4449 void
4450 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4452 Relation rel;
4453 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4455 rel = relation_open(relid, lockmode);
4457 EventTriggerAlterTableRelid(relid);
4459 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4463 * AlterTableGetLockLevel
4465 * Sets the overall lock level required for the supplied list of subcommands.
4466 * Policy for doing this set according to needs of AlterTable(), see
4467 * comments there for overall explanation.
4469 * Function is called before and after parsing, so it must give same
4470 * answer each time it is called. Some subcommands are transformed
4471 * into other subcommand types, so the transform must never be made to a
4472 * lower lock level than previously assigned. All transforms are noted below.
4474 * Since this is called before we lock the table we cannot use table metadata
4475 * to influence the type of lock we acquire.
4477 * There should be no lockmodes hardcoded into the subcommand functions. All
4478 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4479 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4480 * and does not travel through this section of code and cannot be combined with
4481 * any of the subcommands given here.
4483 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4484 * so any changes that might affect SELECTs running on standbys need to use
4485 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4486 * have a solution for that also.
4488 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4489 * that takes a lock less than AccessExclusiveLock can change object definitions
4490 * while pg_dump is running. Be careful to check that the appropriate data is
4491 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4492 * otherwise we might end up with an inconsistent dump that can't restore.
4494 LOCKMODE
4495 AlterTableGetLockLevel(List *cmds)
4498 * This only works if we read catalog tables using MVCC snapshots.
4500 ListCell *lcmd;
4501 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4503 foreach(lcmd, cmds)
4505 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4506 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4508 switch (cmd->subtype)
4511 * These subcommands rewrite the heap, so require full locks.
4513 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4514 * to SELECT */
4515 case AT_SetAccessMethod: /* must rewrite heap */
4516 case AT_SetTableSpace: /* must rewrite heap */
4517 case AT_AlterColumnType: /* must rewrite heap */
4518 cmd_lockmode = AccessExclusiveLock;
4519 break;
4522 * These subcommands may require addition of toast tables. If
4523 * we add a toast table to a table currently being scanned, we
4524 * might miss data added to the new toast table by concurrent
4525 * insert transactions.
4527 case AT_SetStorage: /* may add toast tables, see
4528 * ATRewriteCatalogs() */
4529 cmd_lockmode = AccessExclusiveLock;
4530 break;
4533 * Removing constraints can affect SELECTs that have been
4534 * optimized assuming the constraint holds true. See also
4535 * CloneFkReferenced.
4537 case AT_DropConstraint: /* as DROP INDEX */
4538 case AT_DropNotNull: /* may change some SQL plans */
4539 cmd_lockmode = AccessExclusiveLock;
4540 break;
4543 * Subcommands that may be visible to concurrent SELECTs
4545 case AT_DropColumn: /* change visible to SELECT */
4546 case AT_AddColumnToView: /* CREATE VIEW */
4547 case AT_DropOids: /* used to equiv to DropColumn */
4548 case AT_EnableAlwaysRule: /* may change SELECT rules */
4549 case AT_EnableReplicaRule: /* may change SELECT rules */
4550 case AT_EnableRule: /* may change SELECT rules */
4551 case AT_DisableRule: /* may change SELECT rules */
4552 cmd_lockmode = AccessExclusiveLock;
4553 break;
4556 * Changing owner may remove implicit SELECT privileges
4558 case AT_ChangeOwner: /* change visible to SELECT */
4559 cmd_lockmode = AccessExclusiveLock;
4560 break;
4563 * Changing foreign table options may affect optimization.
4565 case AT_GenericOptions:
4566 case AT_AlterColumnGenericOptions:
4567 cmd_lockmode = AccessExclusiveLock;
4568 break;
4571 * These subcommands affect write operations only.
4573 case AT_EnableTrig:
4574 case AT_EnableAlwaysTrig:
4575 case AT_EnableReplicaTrig:
4576 case AT_EnableTrigAll:
4577 case AT_EnableTrigUser:
4578 case AT_DisableTrig:
4579 case AT_DisableTrigAll:
4580 case AT_DisableTrigUser:
4581 cmd_lockmode = ShareRowExclusiveLock;
4582 break;
4585 * These subcommands affect write operations only. XXX
4586 * Theoretically, these could be ShareRowExclusiveLock.
4588 case AT_ColumnDefault:
4589 case AT_CookedColumnDefault:
4590 case AT_AlterConstraint:
4591 case AT_AddIndex: /* from ADD CONSTRAINT */
4592 case AT_AddIndexConstraint:
4593 case AT_ReplicaIdentity:
4594 case AT_SetNotNull:
4595 case AT_SetAttNotNull:
4596 case AT_EnableRowSecurity:
4597 case AT_DisableRowSecurity:
4598 case AT_ForceRowSecurity:
4599 case AT_NoForceRowSecurity:
4600 case AT_AddIdentity:
4601 case AT_DropIdentity:
4602 case AT_SetIdentity:
4603 case AT_SetExpression:
4604 case AT_DropExpression:
4605 case AT_SetCompression:
4606 cmd_lockmode = AccessExclusiveLock;
4607 break;
4609 case AT_AddConstraint:
4610 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4611 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4612 if (IsA(cmd->def, Constraint))
4614 Constraint *con = (Constraint *) cmd->def;
4616 switch (con->contype)
4618 case CONSTR_EXCLUSION:
4619 case CONSTR_PRIMARY:
4620 case CONSTR_UNIQUE:
4623 * Cases essentially the same as CREATE INDEX. We
4624 * could reduce the lock strength to ShareLock if
4625 * we can work out how to allow concurrent catalog
4626 * updates. XXX Might be set down to
4627 * ShareRowExclusiveLock but requires further
4628 * analysis.
4630 cmd_lockmode = AccessExclusiveLock;
4631 break;
4632 case CONSTR_FOREIGN:
4635 * We add triggers to both tables when we add a
4636 * Foreign Key, so the lock level must be at least
4637 * as strong as CREATE TRIGGER.
4639 cmd_lockmode = ShareRowExclusiveLock;
4640 break;
4642 default:
4643 cmd_lockmode = AccessExclusiveLock;
4646 break;
4649 * These subcommands affect inheritance behaviour. Queries
4650 * started before us will continue to see the old inheritance
4651 * behaviour, while queries started after we commit will see
4652 * new behaviour. No need to prevent reads or writes to the
4653 * subtable while we hook it up though. Changing the TupDesc
4654 * may be a problem, so keep highest lock.
4656 case AT_AddInherit:
4657 case AT_DropInherit:
4658 cmd_lockmode = AccessExclusiveLock;
4659 break;
4662 * These subcommands affect implicit row type conversion. They
4663 * have affects similar to CREATE/DROP CAST on queries. don't
4664 * provide for invalidating parse trees as a result of such
4665 * changes, so we keep these at AccessExclusiveLock.
4667 case AT_AddOf:
4668 case AT_DropOf:
4669 cmd_lockmode = AccessExclusiveLock;
4670 break;
4673 * Only used by CREATE OR REPLACE VIEW which must conflict
4674 * with an SELECTs currently using the view.
4676 case AT_ReplaceRelOptions:
4677 cmd_lockmode = AccessExclusiveLock;
4678 break;
4681 * These subcommands affect general strategies for performance
4682 * and maintenance, though don't change the semantic results
4683 * from normal data reads and writes. Delaying an ALTER TABLE
4684 * behind currently active writes only delays the point where
4685 * the new strategy begins to take effect, so there is no
4686 * benefit in waiting. In this case the minimum restriction
4687 * applies: we don't currently allow concurrent catalog
4688 * updates.
4690 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4691 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4692 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4693 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4694 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4695 cmd_lockmode = ShareUpdateExclusiveLock;
4696 break;
4698 case AT_SetLogged:
4699 case AT_SetUnLogged:
4700 cmd_lockmode = AccessExclusiveLock;
4701 break;
4703 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4704 cmd_lockmode = ShareUpdateExclusiveLock;
4705 break;
4708 * Rel options are more complex than first appears. Options
4709 * are set here for tables, views and indexes; for historical
4710 * reasons these can all be used with ALTER TABLE, so we can't
4711 * decide between them using the basic grammar.
4713 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4714 * getTables() */
4715 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4716 * getTables() */
4717 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4718 break;
4720 case AT_AttachPartition:
4721 cmd_lockmode = ShareUpdateExclusiveLock;
4722 break;
4724 case AT_DetachPartition:
4725 if (((PartitionCmd *) cmd->def)->concurrent)
4726 cmd_lockmode = ShareUpdateExclusiveLock;
4727 else
4728 cmd_lockmode = AccessExclusiveLock;
4729 break;
4731 case AT_DetachPartitionFinalize:
4732 cmd_lockmode = ShareUpdateExclusiveLock;
4733 break;
4735 default: /* oops */
4736 elog(ERROR, "unrecognized alter table type: %d",
4737 (int) cmd->subtype);
4738 break;
4742 * Take the greatest lockmode from any subcommand
4744 if (cmd_lockmode > lockmode)
4745 lockmode = cmd_lockmode;
4748 return lockmode;
4752 * ATController provides top level control over the phases.
4754 * parsetree is passed in to allow it to be passed to event triggers
4755 * when requested.
4757 static void
4758 ATController(AlterTableStmt *parsetree,
4759 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4760 AlterTableUtilityContext *context)
4762 List *wqueue = NIL;
4763 ListCell *lcmd;
4765 /* Phase 1: preliminary examination of commands, create work queue */
4766 foreach(lcmd, cmds)
4768 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4770 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4773 /* Close the relation, but keep lock until commit */
4774 relation_close(rel, NoLock);
4776 /* Phase 2: update system catalogs */
4777 ATRewriteCatalogs(&wqueue, lockmode, context);
4779 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4780 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4784 * ATPrepCmd
4786 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4787 * recursion and permission checks.
4789 * Caller must have acquired appropriate lock type on relation already.
4790 * This lock should be held until commit.
4792 static void
4793 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4794 bool recurse, bool recursing, LOCKMODE lockmode,
4795 AlterTableUtilityContext *context)
4797 AlteredTableInfo *tab;
4798 AlterTablePass pass = AT_PASS_UNSET;
4800 /* Find or create work queue entry for this table */
4801 tab = ATGetQueueEntry(wqueue, rel);
4804 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4805 * partitions that are pending detach.
4807 if (rel->rd_rel->relispartition &&
4808 cmd->subtype != AT_DetachPartitionFinalize &&
4809 PartitionHasPendingDetach(RelationGetRelid(rel)))
4810 ereport(ERROR,
4811 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4812 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4813 RelationGetRelationName(rel)),
4814 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4817 * Copy the original subcommand for each table, so we can scribble on it.
4818 * This avoids conflicts when different child tables need to make
4819 * different parse transformations (for example, the same column may have
4820 * different column numbers in different children).
4822 cmd = copyObject(cmd);
4825 * Do permissions and relkind checking, recursion to child tables if
4826 * needed, and any additional phase-1 processing needed. (But beware of
4827 * adding any processing that looks at table details that another
4828 * subcommand could change. In some cases we reject multiple subcommands
4829 * that could try to change the same state in contrary ways.)
4831 switch (cmd->subtype)
4833 case AT_AddColumn: /* ADD COLUMN */
4834 ATSimplePermissions(cmd->subtype, rel,
4835 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4836 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4837 lockmode, context);
4838 /* Recursion occurs during execution phase */
4839 pass = AT_PASS_ADD_COL;
4840 break;
4841 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4842 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4843 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4844 lockmode, context);
4845 /* Recursion occurs during execution phase */
4846 pass = AT_PASS_ADD_COL;
4847 break;
4848 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4851 * We allow defaults on views so that INSERT into a view can have
4852 * default-ish behavior. This works because the rewriter
4853 * substitutes default values into INSERTs before it expands
4854 * rules.
4856 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4857 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4858 /* No command-specific prep needed */
4859 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4860 break;
4861 case AT_CookedColumnDefault: /* add a pre-cooked default */
4862 /* This is currently used only in CREATE TABLE */
4863 /* (so the permission check really isn't necessary) */
4864 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4865 /* This command never recurses */
4866 pass = AT_PASS_ADD_OTHERCONSTR;
4867 break;
4868 case AT_AddIdentity:
4869 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4870 /* Set up recursion for phase 2; no other prep needed */
4871 if (recurse)
4872 cmd->recurse = true;
4873 pass = AT_PASS_ADD_OTHERCONSTR;
4874 break;
4875 case AT_SetIdentity:
4876 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4877 /* Set up recursion for phase 2; no other prep needed */
4878 if (recurse)
4879 cmd->recurse = true;
4880 /* This should run after AddIdentity, so do it in MISC pass */
4881 pass = AT_PASS_MISC;
4882 break;
4883 case AT_DropIdentity:
4884 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4885 /* Set up recursion for phase 2; no other prep needed */
4886 if (recurse)
4887 cmd->recurse = true;
4888 pass = AT_PASS_DROP;
4889 break;
4890 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4891 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4892 /* Set up recursion for phase 2; no other prep needed */
4893 if (recurse)
4894 cmd->recurse = true;
4895 pass = AT_PASS_DROP;
4896 break;
4897 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4898 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4899 /* Set up recursion for phase 2; no other prep needed */
4900 if (recurse)
4901 cmd->recurse = true;
4902 pass = AT_PASS_COL_ATTRS;
4903 break;
4904 case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4905 * a constraint */
4906 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4907 /* Need command-specific recursion decision */
4908 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4909 pass = AT_PASS_COL_ATTRS;
4910 break;
4911 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4912 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4913 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4914 pass = AT_PASS_SET_EXPRESSION;
4915 break;
4916 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4917 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4918 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4919 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4920 pass = AT_PASS_DROP;
4921 break;
4922 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4923 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4924 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4925 /* No command-specific prep needed */
4926 pass = AT_PASS_MISC;
4927 break;
4928 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4929 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4930 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4931 /* This command never recurses */
4932 pass = AT_PASS_MISC;
4933 break;
4934 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4935 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4936 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4937 /* No command-specific prep needed */
4938 pass = AT_PASS_MISC;
4939 break;
4940 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4941 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4942 /* This command never recurses */
4943 /* No command-specific prep needed */
4944 pass = AT_PASS_MISC;
4945 break;
4946 case AT_DropColumn: /* DROP COLUMN */
4947 ATSimplePermissions(cmd->subtype, rel,
4948 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4949 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4950 lockmode, context);
4951 /* Recursion occurs during execution phase */
4952 pass = AT_PASS_DROP;
4953 break;
4954 case AT_AddIndex: /* ADD INDEX */
4955 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4956 /* This command never recurses */
4957 /* No command-specific prep needed */
4958 pass = AT_PASS_ADD_INDEX;
4959 break;
4960 case AT_AddConstraint: /* ADD CONSTRAINT */
4961 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4962 /* Recursion occurs during execution phase */
4963 /* No command-specific prep needed except saving recurse flag */
4964 if (recurse)
4965 cmd->recurse = true;
4966 pass = AT_PASS_ADD_CONSTR;
4967 break;
4968 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4969 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4970 /* This command never recurses */
4971 /* No command-specific prep needed */
4972 pass = AT_PASS_ADD_INDEXCONSTR;
4973 break;
4974 case AT_DropConstraint: /* DROP CONSTRAINT */
4975 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4976 ATCheckPartitionsNotInUse(rel, lockmode);
4977 /* Other recursion occurs during execution phase */
4978 /* No command-specific prep needed except saving recurse flag */
4979 if (recurse)
4980 cmd->recurse = true;
4981 pass = AT_PASS_DROP;
4982 break;
4983 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4984 ATSimplePermissions(cmd->subtype, rel,
4985 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4986 /* See comments for ATPrepAlterColumnType */
4987 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4988 AT_PASS_UNSET, context);
4989 Assert(cmd != NULL);
4990 /* Performs own recursion */
4991 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4992 lockmode, context);
4993 pass = AT_PASS_ALTER_TYPE;
4994 break;
4995 case AT_AlterColumnGenericOptions:
4996 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4997 /* This command never recurses */
4998 /* No command-specific prep needed */
4999 pass = AT_PASS_MISC;
5000 break;
5001 case AT_ChangeOwner: /* ALTER OWNER */
5002 /* This command never recurses */
5003 /* No command-specific prep needed */
5004 pass = AT_PASS_MISC;
5005 break;
5006 case AT_ClusterOn: /* CLUSTER ON */
5007 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5008 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5009 /* These commands never recurse */
5010 /* No command-specific prep needed */
5011 pass = AT_PASS_MISC;
5012 break;
5013 case AT_SetLogged: /* SET LOGGED */
5014 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5015 if (tab->chgPersistence)
5016 ereport(ERROR,
5017 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5018 errmsg("cannot change persistence setting twice")));
5019 tab->chgPersistence = ATPrepChangePersistence(rel, true);
5020 /* force rewrite if necessary; see comment in ATRewriteTables */
5021 if (tab->chgPersistence)
5023 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5024 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5026 pass = AT_PASS_MISC;
5027 break;
5028 case AT_SetUnLogged: /* SET UNLOGGED */
5029 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5030 if (tab->chgPersistence)
5031 ereport(ERROR,
5032 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5033 errmsg("cannot change persistence setting twice")));
5034 tab->chgPersistence = ATPrepChangePersistence(rel, false);
5035 /* force rewrite if necessary; see comment in ATRewriteTables */
5036 if (tab->chgPersistence)
5038 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5039 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5041 pass = AT_PASS_MISC;
5042 break;
5043 case AT_DropOids: /* SET WITHOUT OIDS */
5044 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5045 pass = AT_PASS_DROP;
5046 break;
5047 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5048 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5050 /* check if another access method change was already requested */
5051 if (tab->chgAccessMethod)
5052 ereport(ERROR,
5053 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5054 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5056 ATPrepSetAccessMethod(tab, rel, cmd->name);
5057 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5058 break;
5059 case AT_SetTableSpace: /* SET TABLESPACE */
5060 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5061 ATT_PARTITIONED_INDEX);
5062 /* This command never recurses */
5063 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5064 pass = AT_PASS_MISC; /* doesn't actually matter */
5065 break;
5066 case AT_SetRelOptions: /* SET (...) */
5067 case AT_ResetRelOptions: /* RESET (...) */
5068 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5069 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5070 /* This command never recurses */
5071 /* No command-specific prep needed */
5072 pass = AT_PASS_MISC;
5073 break;
5074 case AT_AddInherit: /* INHERIT */
5075 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5076 /* This command never recurses */
5077 ATPrepAddInherit(rel);
5078 pass = AT_PASS_MISC;
5079 break;
5080 case AT_DropInherit: /* NO INHERIT */
5081 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5082 /* This command never recurses */
5083 /* No command-specific prep needed */
5084 pass = AT_PASS_MISC;
5085 break;
5086 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5087 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5088 /* Recursion occurs during execution phase */
5089 pass = AT_PASS_MISC;
5090 break;
5091 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5092 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5093 /* Recursion occurs during execution phase */
5094 /* No command-specific prep needed except saving recurse flag */
5095 if (recurse)
5096 cmd->recurse = true;
5097 pass = AT_PASS_MISC;
5098 break;
5099 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5100 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5101 pass = AT_PASS_MISC;
5102 /* This command never recurses */
5103 /* No command-specific prep needed */
5104 break;
5105 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5106 case AT_EnableAlwaysTrig:
5107 case AT_EnableReplicaTrig:
5108 case AT_EnableTrigAll:
5109 case AT_EnableTrigUser:
5110 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5111 case AT_DisableTrigAll:
5112 case AT_DisableTrigUser:
5113 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5114 /* Set up recursion for phase 2; no other prep needed */
5115 if (recurse)
5116 cmd->recurse = true;
5117 pass = AT_PASS_MISC;
5118 break;
5119 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5120 case AT_EnableAlwaysRule:
5121 case AT_EnableReplicaRule:
5122 case AT_DisableRule:
5123 case AT_AddOf: /* OF */
5124 case AT_DropOf: /* NOT OF */
5125 case AT_EnableRowSecurity:
5126 case AT_DisableRowSecurity:
5127 case AT_ForceRowSecurity:
5128 case AT_NoForceRowSecurity:
5129 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5130 /* These commands never recurse */
5131 /* No command-specific prep needed */
5132 pass = AT_PASS_MISC;
5133 break;
5134 case AT_GenericOptions:
5135 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5136 /* No command-specific prep needed */
5137 pass = AT_PASS_MISC;
5138 break;
5139 case AT_AttachPartition:
5140 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5141 /* No command-specific prep needed */
5142 pass = AT_PASS_MISC;
5143 break;
5144 case AT_DetachPartition:
5145 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5146 /* No command-specific prep needed */
5147 pass = AT_PASS_MISC;
5148 break;
5149 case AT_DetachPartitionFinalize:
5150 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5151 /* No command-specific prep needed */
5152 pass = AT_PASS_MISC;
5153 break;
5154 default: /* oops */
5155 elog(ERROR, "unrecognized alter table type: %d",
5156 (int) cmd->subtype);
5157 pass = AT_PASS_UNSET; /* keep compiler quiet */
5158 break;
5160 Assert(pass > AT_PASS_UNSET);
5162 /* Add the subcommand to the appropriate list for phase 2 */
5163 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5167 * ATRewriteCatalogs
5169 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5170 * dispatched in a "safe" execution order (designed to avoid unnecessary
5171 * conflicts).
5173 static void
5174 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5175 AlterTableUtilityContext *context)
5177 ListCell *ltab;
5180 * We process all the tables "in parallel", one pass at a time. This is
5181 * needed because we may have to propagate work from one table to another
5182 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5183 * re-adding of the foreign key constraint to the other table). Work can
5184 * only be propagated into later passes, however.
5186 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5188 /* Go through each table that needs to be processed */
5189 foreach(ltab, *wqueue)
5191 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5192 List *subcmds = tab->subcmds[pass];
5193 ListCell *lcmd;
5195 if (subcmds == NIL)
5196 continue;
5199 * Open the relation and store it in tab. This allows subroutines
5200 * close and reopen, if necessary. Appropriate lock was obtained
5201 * by phase 1, needn't get it again.
5203 tab->rel = relation_open(tab->relid, NoLock);
5205 foreach(lcmd, subcmds)
5206 ATExecCmd(wqueue, tab,
5207 lfirst_node(AlterTableCmd, lcmd),
5208 lockmode, pass, context);
5211 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5212 * (this is not done in ATExecAlterColumnType since it should be
5213 * done only once if multiple columns of a table are altered).
5215 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5216 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5218 if (tab->rel)
5220 relation_close(tab->rel, NoLock);
5221 tab->rel = NULL;
5226 /* Check to see if a toast table must be added. */
5227 foreach(ltab, *wqueue)
5229 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5232 * If the table is source table of ATTACH PARTITION command, we did
5233 * not modify anything about it that will change its toasting
5234 * requirement, so no need to check.
5236 if (((tab->relkind == RELKIND_RELATION ||
5237 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5238 tab->partition_constraint == NULL) ||
5239 tab->relkind == RELKIND_MATVIEW)
5240 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5245 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5247 static void
5248 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5249 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5250 AlterTableUtilityContext *context)
5252 ObjectAddress address = InvalidObjectAddress;
5253 Relation rel = tab->rel;
5255 switch (cmd->subtype)
5257 case AT_AddColumn: /* ADD COLUMN */
5258 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5259 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5260 cmd->recurse, false,
5261 lockmode, cur_pass, context);
5262 break;
5263 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5264 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5265 break;
5266 case AT_CookedColumnDefault: /* add a pre-cooked default */
5267 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5268 break;
5269 case AT_AddIdentity:
5270 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5271 cur_pass, context);
5272 Assert(cmd != NULL);
5273 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5274 break;
5275 case AT_SetIdentity:
5276 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5277 cur_pass, context);
5278 Assert(cmd != NULL);
5279 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5280 break;
5281 case AT_DropIdentity:
5282 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5283 break;
5284 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5285 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5286 break;
5287 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5288 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5289 cmd->recurse, false, NULL, lockmode);
5290 break;
5291 case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5292 address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5293 break;
5294 case AT_SetExpression:
5295 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5296 break;
5297 case AT_DropExpression:
5298 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5299 break;
5300 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5301 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5302 break;
5303 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5304 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5305 break;
5306 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5307 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5308 break;
5309 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5310 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5311 break;
5312 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5313 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5314 lockmode);
5315 break;
5316 case AT_DropColumn: /* DROP COLUMN */
5317 address = ATExecDropColumn(wqueue, rel, cmd->name,
5318 cmd->behavior, cmd->recurse, false,
5319 cmd->missing_ok, lockmode,
5320 NULL);
5321 break;
5322 case AT_AddIndex: /* ADD INDEX */
5323 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5324 lockmode);
5325 break;
5326 case AT_ReAddIndex: /* ADD INDEX */
5327 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5328 lockmode);
5329 break;
5330 case AT_ReAddStatistics: /* ADD STATISTICS */
5331 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5332 true, lockmode);
5333 break;
5334 case AT_AddConstraint: /* ADD CONSTRAINT */
5335 /* Transform the command only during initial examination */
5336 if (cur_pass == AT_PASS_ADD_CONSTR)
5337 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5338 cmd->recurse, lockmode,
5339 cur_pass, context);
5340 /* Depending on constraint type, might be no more work to do now */
5341 if (cmd != NULL)
5342 address =
5343 ATExecAddConstraint(wqueue, tab, rel,
5344 (Constraint *) cmd->def,
5345 cmd->recurse, false, lockmode);
5346 break;
5347 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5348 address =
5349 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5350 true, true, lockmode);
5351 break;
5352 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5353 * constraint */
5354 address =
5355 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5356 ((AlterDomainStmt *) cmd->def)->def,
5357 NULL);
5358 break;
5359 case AT_ReAddComment: /* Re-add existing comment */
5360 address = CommentObject((CommentStmt *) cmd->def);
5361 break;
5362 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5363 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5364 lockmode);
5365 break;
5366 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5367 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5368 break;
5369 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5370 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5371 false, lockmode);
5372 break;
5373 case AT_DropConstraint: /* DROP CONSTRAINT */
5374 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5375 cmd->recurse,
5376 cmd->missing_ok, lockmode);
5377 break;
5378 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5379 /* parse transformation was done earlier */
5380 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5381 break;
5382 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5383 address =
5384 ATExecAlterColumnGenericOptions(rel, cmd->name,
5385 (List *) cmd->def, lockmode);
5386 break;
5387 case AT_ChangeOwner: /* ALTER OWNER */
5388 ATExecChangeOwner(RelationGetRelid(rel),
5389 get_rolespec_oid(cmd->newowner, false),
5390 false, lockmode);
5391 break;
5392 case AT_ClusterOn: /* CLUSTER ON */
5393 address = ATExecClusterOn(rel, cmd->name, lockmode);
5394 break;
5395 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5396 ATExecDropCluster(rel, lockmode);
5397 break;
5398 case AT_SetLogged: /* SET LOGGED */
5399 case AT_SetUnLogged: /* SET UNLOGGED */
5400 break;
5401 case AT_DropOids: /* SET WITHOUT OIDS */
5402 /* nothing to do here, oid columns don't exist anymore */
5403 break;
5404 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5407 * Only do this for partitioned tables, for which this is just a
5408 * catalog change. Tables with storage are handled by Phase 3.
5410 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5411 tab->chgAccessMethod)
5412 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5413 break;
5414 case AT_SetTableSpace: /* SET TABLESPACE */
5417 * Only do this for partitioned tables and indexes, for which this
5418 * is just a catalog change. Other relation types which have
5419 * storage are handled by Phase 3.
5421 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5422 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5423 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5425 break;
5426 case AT_SetRelOptions: /* SET (...) */
5427 case AT_ResetRelOptions: /* RESET (...) */
5428 case AT_ReplaceRelOptions: /* replace entire option list */
5429 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5430 break;
5431 case AT_EnableTrig: /* ENABLE TRIGGER name */
5432 ATExecEnableDisableTrigger(rel, cmd->name,
5433 TRIGGER_FIRES_ON_ORIGIN, false,
5434 cmd->recurse,
5435 lockmode);
5436 break;
5437 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5438 ATExecEnableDisableTrigger(rel, cmd->name,
5439 TRIGGER_FIRES_ALWAYS, false,
5440 cmd->recurse,
5441 lockmode);
5442 break;
5443 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5444 ATExecEnableDisableTrigger(rel, cmd->name,
5445 TRIGGER_FIRES_ON_REPLICA, false,
5446 cmd->recurse,
5447 lockmode);
5448 break;
5449 case AT_DisableTrig: /* DISABLE TRIGGER name */
5450 ATExecEnableDisableTrigger(rel, cmd->name,
5451 TRIGGER_DISABLED, false,
5452 cmd->recurse,
5453 lockmode);
5454 break;
5455 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5456 ATExecEnableDisableTrigger(rel, NULL,
5457 TRIGGER_FIRES_ON_ORIGIN, false,
5458 cmd->recurse,
5459 lockmode);
5460 break;
5461 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5462 ATExecEnableDisableTrigger(rel, NULL,
5463 TRIGGER_DISABLED, false,
5464 cmd->recurse,
5465 lockmode);
5466 break;
5467 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5468 ATExecEnableDisableTrigger(rel, NULL,
5469 TRIGGER_FIRES_ON_ORIGIN, true,
5470 cmd->recurse,
5471 lockmode);
5472 break;
5473 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5474 ATExecEnableDisableTrigger(rel, NULL,
5475 TRIGGER_DISABLED, true,
5476 cmd->recurse,
5477 lockmode);
5478 break;
5480 case AT_EnableRule: /* ENABLE RULE name */
5481 ATExecEnableDisableRule(rel, cmd->name,
5482 RULE_FIRES_ON_ORIGIN, lockmode);
5483 break;
5484 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5485 ATExecEnableDisableRule(rel, cmd->name,
5486 RULE_FIRES_ALWAYS, lockmode);
5487 break;
5488 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5489 ATExecEnableDisableRule(rel, cmd->name,
5490 RULE_FIRES_ON_REPLICA, lockmode);
5491 break;
5492 case AT_DisableRule: /* DISABLE RULE name */
5493 ATExecEnableDisableRule(rel, cmd->name,
5494 RULE_DISABLED, lockmode);
5495 break;
5497 case AT_AddInherit:
5498 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5499 break;
5500 case AT_DropInherit:
5501 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5502 break;
5503 case AT_AddOf:
5504 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5505 break;
5506 case AT_DropOf:
5507 ATExecDropOf(rel, lockmode);
5508 break;
5509 case AT_ReplicaIdentity:
5510 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5511 break;
5512 case AT_EnableRowSecurity:
5513 ATExecSetRowSecurity(rel, true);
5514 break;
5515 case AT_DisableRowSecurity:
5516 ATExecSetRowSecurity(rel, false);
5517 break;
5518 case AT_ForceRowSecurity:
5519 ATExecForceNoForceRowSecurity(rel, true);
5520 break;
5521 case AT_NoForceRowSecurity:
5522 ATExecForceNoForceRowSecurity(rel, false);
5523 break;
5524 case AT_GenericOptions:
5525 ATExecGenericOptions(rel, (List *) cmd->def);
5526 break;
5527 case AT_AttachPartition:
5528 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5529 cur_pass, context);
5530 Assert(cmd != NULL);
5531 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5532 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5533 context);
5534 else
5535 address = ATExecAttachPartitionIdx(wqueue, rel,
5536 ((PartitionCmd *) cmd->def)->name);
5537 break;
5538 case AT_DetachPartition:
5539 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5540 cur_pass, context);
5541 Assert(cmd != NULL);
5542 /* ATPrepCmd ensures it must be a table */
5543 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5544 address = ATExecDetachPartition(wqueue, tab, rel,
5545 ((PartitionCmd *) cmd->def)->name,
5546 ((PartitionCmd *) cmd->def)->concurrent);
5547 break;
5548 case AT_DetachPartitionFinalize:
5549 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5550 break;
5551 default: /* oops */
5552 elog(ERROR, "unrecognized alter table type: %d",
5553 (int) cmd->subtype);
5554 break;
5558 * Report the subcommand to interested event triggers.
5560 if (cmd)
5561 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5564 * Bump the command counter to ensure the next subcommand in the sequence
5565 * can see the changes so far
5567 CommandCounterIncrement();
5571 * ATParseTransformCmd: perform parse transformation for one subcommand
5573 * Returns the transformed subcommand tree, if there is one, else NULL.
5575 * The parser may hand back additional AlterTableCmd(s) and/or other
5576 * utility statements, either before or after the original subcommand.
5577 * Other AlterTableCmds are scheduled into the appropriate slot of the
5578 * AlteredTableInfo (they had better be for later passes than the current one).
5579 * Utility statements that are supposed to happen before the AlterTableCmd
5580 * are executed immediately. Those that are supposed to happen afterwards
5581 * are added to the tab->afterStmts list to be done at the very end.
5583 static AlterTableCmd *
5584 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5585 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5586 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5588 AlterTableCmd *newcmd = NULL;
5589 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5590 List *beforeStmts;
5591 List *afterStmts;
5592 ListCell *lc;
5594 /* Gin up an AlterTableStmt with just this subcommand and this table */
5595 atstmt->relation =
5596 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5597 pstrdup(RelationGetRelationName(rel)),
5598 -1);
5599 atstmt->relation->inh = recurse;
5600 atstmt->cmds = list_make1(cmd);
5601 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5602 atstmt->missing_ok = false;
5604 /* Transform the AlterTableStmt */
5605 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5606 atstmt,
5607 context->queryString,
5608 &beforeStmts,
5609 &afterStmts);
5611 /* Execute any statements that should happen before these subcommand(s) */
5612 foreach(lc, beforeStmts)
5614 Node *stmt = (Node *) lfirst(lc);
5616 ProcessUtilityForAlterTable(stmt, context);
5617 CommandCounterIncrement();
5620 /* Examine the transformed subcommands and schedule them appropriately */
5621 foreach(lc, atstmt->cmds)
5623 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5624 AlterTablePass pass;
5627 * This switch need only cover the subcommand types that can be added
5628 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5629 * executing the subcommand immediately, as a substitute for the
5630 * original subcommand. (Note, however, that this does cause
5631 * AT_AddConstraint subcommands to be rescheduled into later passes,
5632 * which is important for index and foreign key constraints.)
5634 * We assume we needn't do any phase-1 checks for added subcommands.
5636 switch (cmd2->subtype)
5638 case AT_SetAttNotNull:
5639 ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5640 pass = AT_PASS_COL_ATTRS;
5641 break;
5642 case AT_AddIndex:
5645 * A primary key on a inheritance parent needs supporting NOT
5646 * NULL constraint on its children; enqueue commands to create
5647 * those or mark them inherited if they already exist.
5649 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5650 pass = AT_PASS_ADD_INDEX;
5651 break;
5652 case AT_AddIndexConstraint:
5653 /* as above */
5654 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5655 pass = AT_PASS_ADD_INDEXCONSTR;
5656 break;
5657 case AT_AddConstraint:
5658 /* Recursion occurs during execution phase */
5659 if (recurse)
5660 cmd2->recurse = true;
5661 switch (castNode(Constraint, cmd2->def)->contype)
5663 case CONSTR_PRIMARY:
5664 case CONSTR_UNIQUE:
5665 case CONSTR_EXCLUSION:
5666 pass = AT_PASS_ADD_INDEXCONSTR;
5667 break;
5668 default:
5669 pass = AT_PASS_ADD_OTHERCONSTR;
5670 break;
5672 break;
5673 case AT_AlterColumnGenericOptions:
5674 /* This command never recurses */
5675 /* No command-specific prep needed */
5676 pass = AT_PASS_MISC;
5677 break;
5678 default:
5679 pass = cur_pass;
5680 break;
5683 if (pass < cur_pass)
5685 /* Cannot schedule into a pass we already finished */
5686 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5687 pass);
5689 else if (pass > cur_pass)
5691 /* OK, queue it up for later */
5692 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5694 else
5697 * We should see at most one subcommand for the current pass,
5698 * which is the transformed version of the original subcommand.
5700 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5702 /* Found the transformed version of our subcommand */
5703 newcmd = cmd2;
5705 else
5706 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5707 pass);
5711 /* Queue up any after-statements to happen at the end */
5712 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5714 return newcmd;
5718 * ATRewriteTables: ALTER TABLE phase 3
5720 static void
5721 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5722 AlterTableUtilityContext *context)
5724 ListCell *ltab;
5726 /* Go through each table that needs to be checked or rewritten */
5727 foreach(ltab, *wqueue)
5729 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5731 /* Relations without storage may be ignored here */
5732 if (!RELKIND_HAS_STORAGE(tab->relkind))
5733 continue;
5736 * If we change column data types, the operation has to be propagated
5737 * to tables that use this table's rowtype as a column type.
5738 * tab->newvals will also be non-NULL in the case where we're adding a
5739 * column with a default. We choose to forbid that case as well,
5740 * since composite types might eventually support defaults.
5742 * (Eventually we'll probably need to check for composite type
5743 * dependencies even when we're just scanning the table without a
5744 * rewrite, but at the moment a composite type does not enforce any
5745 * constraints, so it's not necessary/appropriate to enforce them just
5746 * during ALTER.)
5748 if (tab->newvals != NIL || tab->rewrite > 0)
5750 Relation rel;
5752 rel = table_open(tab->relid, NoLock);
5753 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5754 table_close(rel, NoLock);
5758 * We only need to rewrite the table if at least one column needs to
5759 * be recomputed, or we are changing its persistence or access method.
5761 * There are two reasons for requiring a rewrite when changing
5762 * persistence: on one hand, we need to ensure that the buffers
5763 * belonging to each of the two relations are marked with or without
5764 * BM_PERMANENT properly. On the other hand, since rewriting creates
5765 * and assigns a new relfilenumber, we automatically create or drop an
5766 * init fork for the relation as appropriate.
5768 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5770 /* Build a temporary relation and copy data */
5771 Relation OldHeap;
5772 Oid OIDNewHeap;
5773 Oid NewAccessMethod;
5774 Oid NewTableSpace;
5775 char persistence;
5777 OldHeap = table_open(tab->relid, NoLock);
5780 * We don't support rewriting of system catalogs; there are too
5781 * many corner cases and too little benefit. In particular this
5782 * is certainly not going to work for mapped catalogs.
5784 if (IsSystemRelation(OldHeap))
5785 ereport(ERROR,
5786 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5787 errmsg("cannot rewrite system relation \"%s\"",
5788 RelationGetRelationName(OldHeap))));
5790 if (RelationIsUsedAsCatalogTable(OldHeap))
5791 ereport(ERROR,
5792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5793 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5794 RelationGetRelationName(OldHeap))));
5797 * Don't allow rewrite on temp tables of other backends ... their
5798 * local buffer manager is not going to cope.
5800 if (RELATION_IS_OTHER_TEMP(OldHeap))
5801 ereport(ERROR,
5802 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5803 errmsg("cannot rewrite temporary tables of other sessions")));
5806 * Select destination tablespace (same as original unless user
5807 * requested a change)
5809 if (tab->newTableSpace)
5810 NewTableSpace = tab->newTableSpace;
5811 else
5812 NewTableSpace = OldHeap->rd_rel->reltablespace;
5815 * Select destination access method (same as original unless user
5816 * requested a change)
5818 if (tab->chgAccessMethod)
5819 NewAccessMethod = tab->newAccessMethod;
5820 else
5821 NewAccessMethod = OldHeap->rd_rel->relam;
5824 * Select persistence of transient table (same as original unless
5825 * user requested a change)
5827 persistence = tab->chgPersistence ?
5828 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5830 table_close(OldHeap, NoLock);
5833 * Fire off an Event Trigger now, before actually rewriting the
5834 * table.
5836 * We don't support Event Trigger for nested commands anywhere,
5837 * here included, and parsetree is given NULL when coming from
5838 * AlterTableInternal.
5840 * And fire it only once.
5842 if (parsetree)
5843 EventTriggerTableRewrite((Node *) parsetree,
5844 tab->relid,
5845 tab->rewrite);
5848 * Create transient table that will receive the modified data.
5850 * Ensure it is marked correctly as logged or unlogged. We have
5851 * to do this here so that buffers for the new relfilenumber will
5852 * have the right persistence set, and at the same time ensure
5853 * that the original filenumbers's buffers will get read in with
5854 * the correct setting (i.e. the original one). Otherwise a
5855 * rollback after the rewrite would possibly result with buffers
5856 * for the original filenumbers having the wrong persistence
5857 * setting.
5859 * NB: This relies on swap_relation_files() also swapping the
5860 * persistence. That wouldn't work for pg_class, but that can't be
5861 * unlogged anyway.
5863 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5864 persistence, lockmode);
5867 * Copy the heap data into the new table with the desired
5868 * modifications, and test the current data within the table
5869 * against new constraints generated by ALTER TABLE commands.
5871 ATRewriteTable(tab, OIDNewHeap, lockmode);
5874 * Swap the physical files of the old and new heaps, then rebuild
5875 * indexes and discard the old heap. We can use RecentXmin for
5876 * the table's new relfrozenxid because we rewrote all the tuples
5877 * in ATRewriteTable, so no older Xid remains in the table. Also,
5878 * we never try to swap toast tables by content, since we have no
5879 * interest in letting this code work on system catalogs.
5881 finish_heap_swap(tab->relid, OIDNewHeap,
5882 false, false, true,
5883 !OidIsValid(tab->newTableSpace),
5884 RecentXmin,
5885 ReadNextMultiXactId(),
5886 persistence);
5888 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5890 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5892 if (tab->chgPersistence)
5893 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5895 else
5898 * If required, test the current data within the table against new
5899 * constraints generated by ALTER TABLE commands, but don't
5900 * rebuild data.
5902 if (tab->constraints != NIL || tab->verify_new_notnull ||
5903 tab->partition_constraint != NULL)
5904 ATRewriteTable(tab, InvalidOid, lockmode);
5907 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5908 * just do a block-by-block copy.
5910 if (tab->newTableSpace)
5911 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5915 * Also change persistence of owned sequences, so that it matches the
5916 * table persistence.
5918 if (tab->chgPersistence)
5920 List *seqlist = getOwnedSequences(tab->relid);
5921 ListCell *lc;
5923 foreach(lc, seqlist)
5925 Oid seq_relid = lfirst_oid(lc);
5927 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5933 * Foreign key constraints are checked in a final pass, since (a) it's
5934 * generally best to examine each one separately, and (b) it's at least
5935 * theoretically possible that we have changed both relations of the
5936 * foreign key, and we'd better have finished both rewrites before we try
5937 * to read the tables.
5939 foreach(ltab, *wqueue)
5941 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5942 Relation rel = NULL;
5943 ListCell *lcon;
5945 /* Relations without storage may be ignored here too */
5946 if (!RELKIND_HAS_STORAGE(tab->relkind))
5947 continue;
5949 foreach(lcon, tab->constraints)
5951 NewConstraint *con = lfirst(lcon);
5953 if (con->contype == CONSTR_FOREIGN)
5955 Constraint *fkconstraint = (Constraint *) con->qual;
5956 Relation refrel;
5958 if (rel == NULL)
5960 /* Long since locked, no need for another */
5961 rel = table_open(tab->relid, NoLock);
5964 refrel = table_open(con->refrelid, RowShareLock);
5966 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5967 con->refindid,
5968 con->conid,
5969 con->conwithperiod);
5972 * No need to mark the constraint row as validated, we did
5973 * that when we inserted the row earlier.
5976 table_close(refrel, NoLock);
5980 if (rel)
5981 table_close(rel, NoLock);
5984 /* Finally, run any afterStmts that were queued up */
5985 foreach(ltab, *wqueue)
5987 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5988 ListCell *lc;
5990 foreach(lc, tab->afterStmts)
5992 Node *stmt = (Node *) lfirst(lc);
5994 ProcessUtilityForAlterTable(stmt, context);
5995 CommandCounterIncrement();
6001 * ATRewriteTable: scan or rewrite one table
6003 * OIDNewHeap is InvalidOid if we don't need to rewrite
6005 static void
6006 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
6008 Relation oldrel;
6009 Relation newrel;
6010 TupleDesc oldTupDesc;
6011 TupleDesc newTupDesc;
6012 bool needscan = false;
6013 List *notnull_attrs;
6014 int i;
6015 ListCell *l;
6016 EState *estate;
6017 CommandId mycid;
6018 BulkInsertState bistate;
6019 int ti_options;
6020 ExprState *partqualstate = NULL;
6023 * Open the relation(s). We have surely already locked the existing
6024 * table.
6026 oldrel = table_open(tab->relid, NoLock);
6027 oldTupDesc = tab->oldDesc;
6028 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6030 if (OidIsValid(OIDNewHeap))
6031 newrel = table_open(OIDNewHeap, lockmode);
6032 else
6033 newrel = NULL;
6036 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6037 * is empty, so don't bother using it.
6039 if (newrel)
6041 mycid = GetCurrentCommandId(true);
6042 bistate = GetBulkInsertState();
6043 ti_options = TABLE_INSERT_SKIP_FSM;
6045 else
6047 /* keep compiler quiet about using these uninitialized */
6048 mycid = 0;
6049 bistate = NULL;
6050 ti_options = 0;
6054 * Generate the constraint and default execution states
6057 estate = CreateExecutorState();
6059 /* Build the needed expression execution states */
6060 foreach(l, tab->constraints)
6062 NewConstraint *con = lfirst(l);
6064 switch (con->contype)
6066 case CONSTR_CHECK:
6067 needscan = true;
6068 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6069 break;
6070 case CONSTR_FOREIGN:
6071 /* Nothing to do here */
6072 break;
6073 default:
6074 elog(ERROR, "unrecognized constraint type: %d",
6075 (int) con->contype);
6079 /* Build expression execution states for partition check quals */
6080 if (tab->partition_constraint)
6082 needscan = true;
6083 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6086 foreach(l, tab->newvals)
6088 NewColumnValue *ex = lfirst(l);
6090 /* expr already planned */
6091 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6094 notnull_attrs = NIL;
6095 if (newrel || tab->verify_new_notnull)
6098 * If we are rebuilding the tuples OR if we added any new but not
6099 * verified not-null constraints, check all not-null constraints. This
6100 * is a bit of overkill but it minimizes risk of bugs, and
6101 * heap_attisnull is a pretty cheap test anyway.
6103 for (i = 0; i < newTupDesc->natts; i++)
6105 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6107 if (attr->attnotnull && !attr->attisdropped)
6108 notnull_attrs = lappend_int(notnull_attrs, i);
6110 if (notnull_attrs)
6111 needscan = true;
6114 if (newrel || needscan)
6116 ExprContext *econtext;
6117 TupleTableSlot *oldslot;
6118 TupleTableSlot *newslot;
6119 TableScanDesc scan;
6120 MemoryContext oldCxt;
6121 List *dropped_attrs = NIL;
6122 ListCell *lc;
6123 Snapshot snapshot;
6125 if (newrel)
6126 ereport(DEBUG1,
6127 (errmsg_internal("rewriting table \"%s\"",
6128 RelationGetRelationName(oldrel))));
6129 else
6130 ereport(DEBUG1,
6131 (errmsg_internal("verifying table \"%s\"",
6132 RelationGetRelationName(oldrel))));
6134 if (newrel)
6137 * All predicate locks on the tuples or pages are about to be made
6138 * invalid, because we move tuples around. Promote them to
6139 * relation locks.
6141 TransferPredicateLocksToHeapRelation(oldrel);
6144 econtext = GetPerTupleExprContext(estate);
6147 * Create necessary tuple slots. When rewriting, two slots are needed,
6148 * otherwise one suffices. In the case where one slot suffices, we
6149 * need to use the new tuple descriptor, otherwise some constraints
6150 * can't be evaluated. Note that even when the tuple layout is the
6151 * same and no rewrite is required, the tupDescs might not be
6152 * (consider ADD COLUMN without a default).
6154 if (tab->rewrite)
6156 Assert(newrel != NULL);
6157 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6158 table_slot_callbacks(oldrel));
6159 newslot = MakeSingleTupleTableSlot(newTupDesc,
6160 table_slot_callbacks(newrel));
6163 * Set all columns in the new slot to NULL initially, to ensure
6164 * columns added as part of the rewrite are initialized to NULL.
6165 * That is necessary as tab->newvals will not contain an
6166 * expression for columns with a NULL default, e.g. when adding a
6167 * column without a default together with a column with a default
6168 * requiring an actual rewrite.
6170 ExecStoreAllNullTuple(newslot);
6172 else
6174 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6175 table_slot_callbacks(oldrel));
6176 newslot = NULL;
6180 * Any attributes that are dropped according to the new tuple
6181 * descriptor can be set to NULL. We precompute the list of dropped
6182 * attributes to avoid needing to do so in the per-tuple loop.
6184 for (i = 0; i < newTupDesc->natts; i++)
6186 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6187 dropped_attrs = lappend_int(dropped_attrs, i);
6191 * Scan through the rows, generating a new row if needed and then
6192 * checking all the constraints.
6194 snapshot = RegisterSnapshot(GetLatestSnapshot());
6195 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6198 * Switch to per-tuple memory context and reset it for each tuple
6199 * produced, so we don't leak memory.
6201 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6203 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6205 TupleTableSlot *insertslot;
6207 if (tab->rewrite > 0)
6209 /* Extract data from old tuple */
6210 slot_getallattrs(oldslot);
6211 ExecClearTuple(newslot);
6213 /* copy attributes */
6214 memcpy(newslot->tts_values, oldslot->tts_values,
6215 sizeof(Datum) * oldslot->tts_nvalid);
6216 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6217 sizeof(bool) * oldslot->tts_nvalid);
6219 /* Set dropped attributes to null in new tuple */
6220 foreach(lc, dropped_attrs)
6221 newslot->tts_isnull[lfirst_int(lc)] = true;
6224 * Constraints and GENERATED expressions might reference the
6225 * tableoid column, so fill tts_tableOid with the desired
6226 * value. (We must do this each time, because it gets
6227 * overwritten with newrel's OID during storing.)
6229 newslot->tts_tableOid = RelationGetRelid(oldrel);
6232 * Process supplied expressions to replace selected columns.
6234 * First, evaluate expressions whose inputs come from the old
6235 * tuple.
6237 econtext->ecxt_scantuple = oldslot;
6239 foreach(l, tab->newvals)
6241 NewColumnValue *ex = lfirst(l);
6243 if (ex->is_generated)
6244 continue;
6246 newslot->tts_values[ex->attnum - 1]
6247 = ExecEvalExpr(ex->exprstate,
6248 econtext,
6249 &newslot->tts_isnull[ex->attnum - 1]);
6252 ExecStoreVirtualTuple(newslot);
6255 * Now, evaluate any expressions whose inputs come from the
6256 * new tuple. We assume these columns won't reference each
6257 * other, so that there's no ordering dependency.
6259 econtext->ecxt_scantuple = newslot;
6261 foreach(l, tab->newvals)
6263 NewColumnValue *ex = lfirst(l);
6265 if (!ex->is_generated)
6266 continue;
6268 newslot->tts_values[ex->attnum - 1]
6269 = ExecEvalExpr(ex->exprstate,
6270 econtext,
6271 &newslot->tts_isnull[ex->attnum - 1]);
6274 insertslot = newslot;
6276 else
6279 * If there's no rewrite, old and new table are guaranteed to
6280 * have the same AM, so we can just use the old slot to verify
6281 * new constraints etc.
6283 insertslot = oldslot;
6286 /* Now check any constraints on the possibly-changed tuple */
6287 econtext->ecxt_scantuple = insertslot;
6289 foreach(l, notnull_attrs)
6291 int attn = lfirst_int(l);
6293 if (slot_attisnull(insertslot, attn + 1))
6295 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6297 ereport(ERROR,
6298 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6299 errmsg("column \"%s\" of relation \"%s\" contains null values",
6300 NameStr(attr->attname),
6301 RelationGetRelationName(oldrel)),
6302 errtablecol(oldrel, attn + 1)));
6306 foreach(l, tab->constraints)
6308 NewConstraint *con = lfirst(l);
6310 switch (con->contype)
6312 case CONSTR_CHECK:
6313 if (!ExecCheck(con->qualstate, econtext))
6314 ereport(ERROR,
6315 (errcode(ERRCODE_CHECK_VIOLATION),
6316 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6317 con->name,
6318 RelationGetRelationName(oldrel)),
6319 errtableconstraint(oldrel, con->name)));
6320 break;
6321 case CONSTR_NOTNULL:
6322 case CONSTR_FOREIGN:
6323 /* Nothing to do here */
6324 break;
6325 default:
6326 elog(ERROR, "unrecognized constraint type: %d",
6327 (int) con->contype);
6331 if (partqualstate && !ExecCheck(partqualstate, econtext))
6333 if (tab->validate_default)
6334 ereport(ERROR,
6335 (errcode(ERRCODE_CHECK_VIOLATION),
6336 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6337 RelationGetRelationName(oldrel)),
6338 errtable(oldrel)));
6339 else
6340 ereport(ERROR,
6341 (errcode(ERRCODE_CHECK_VIOLATION),
6342 errmsg("partition constraint of relation \"%s\" is violated by some row",
6343 RelationGetRelationName(oldrel)),
6344 errtable(oldrel)));
6347 /* Write the tuple out to the new relation */
6348 if (newrel)
6349 table_tuple_insert(newrel, insertslot, mycid,
6350 ti_options, bistate);
6352 ResetExprContext(econtext);
6354 CHECK_FOR_INTERRUPTS();
6357 MemoryContextSwitchTo(oldCxt);
6358 table_endscan(scan);
6359 UnregisterSnapshot(snapshot);
6361 ExecDropSingleTupleTableSlot(oldslot);
6362 if (newslot)
6363 ExecDropSingleTupleTableSlot(newslot);
6366 FreeExecutorState(estate);
6368 table_close(oldrel, NoLock);
6369 if (newrel)
6371 FreeBulkInsertState(bistate);
6373 table_finish_bulk_insert(newrel, ti_options);
6375 table_close(newrel, NoLock);
6380 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6382 static AlteredTableInfo *
6383 ATGetQueueEntry(List **wqueue, Relation rel)
6385 Oid relid = RelationGetRelid(rel);
6386 AlteredTableInfo *tab;
6387 ListCell *ltab;
6389 foreach(ltab, *wqueue)
6391 tab = (AlteredTableInfo *) lfirst(ltab);
6392 if (tab->relid == relid)
6393 return tab;
6397 * Not there, so add it. Note that we make a copy of the relation's
6398 * existing descriptor before anything interesting can happen to it.
6400 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6401 tab->relid = relid;
6402 tab->rel = NULL; /* set later */
6403 tab->relkind = rel->rd_rel->relkind;
6404 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6405 tab->newAccessMethod = InvalidOid;
6406 tab->chgAccessMethod = false;
6407 tab->newTableSpace = InvalidOid;
6408 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6409 tab->chgPersistence = false;
6411 *wqueue = lappend(*wqueue, tab);
6413 return tab;
6416 static const char *
6417 alter_table_type_to_string(AlterTableType cmdtype)
6419 switch (cmdtype)
6421 case AT_AddColumn:
6422 case AT_AddColumnToView:
6423 return "ADD COLUMN";
6424 case AT_ColumnDefault:
6425 case AT_CookedColumnDefault:
6426 return "ALTER COLUMN ... SET DEFAULT";
6427 case AT_DropNotNull:
6428 return "ALTER COLUMN ... DROP NOT NULL";
6429 case AT_SetNotNull:
6430 return "ALTER COLUMN ... SET NOT NULL";
6431 case AT_SetAttNotNull:
6432 return NULL; /* not real grammar */
6433 case AT_SetExpression:
6434 return "ALTER COLUMN ... SET EXPRESSION";
6435 case AT_DropExpression:
6436 return "ALTER COLUMN ... DROP EXPRESSION";
6437 case AT_SetStatistics:
6438 return "ALTER COLUMN ... SET STATISTICS";
6439 case AT_SetOptions:
6440 return "ALTER COLUMN ... SET";
6441 case AT_ResetOptions:
6442 return "ALTER COLUMN ... RESET";
6443 case AT_SetStorage:
6444 return "ALTER COLUMN ... SET STORAGE";
6445 case AT_SetCompression:
6446 return "ALTER COLUMN ... SET COMPRESSION";
6447 case AT_DropColumn:
6448 return "DROP COLUMN";
6449 case AT_AddIndex:
6450 case AT_ReAddIndex:
6451 return NULL; /* not real grammar */
6452 case AT_AddConstraint:
6453 case AT_ReAddConstraint:
6454 case AT_ReAddDomainConstraint:
6455 case AT_AddIndexConstraint:
6456 return "ADD CONSTRAINT";
6457 case AT_AlterConstraint:
6458 return "ALTER CONSTRAINT";
6459 case AT_ValidateConstraint:
6460 return "VALIDATE CONSTRAINT";
6461 case AT_DropConstraint:
6462 return "DROP CONSTRAINT";
6463 case AT_ReAddComment:
6464 return NULL; /* not real grammar */
6465 case AT_AlterColumnType:
6466 return "ALTER COLUMN ... SET DATA TYPE";
6467 case AT_AlterColumnGenericOptions:
6468 return "ALTER COLUMN ... OPTIONS";
6469 case AT_ChangeOwner:
6470 return "OWNER TO";
6471 case AT_ClusterOn:
6472 return "CLUSTER ON";
6473 case AT_DropCluster:
6474 return "SET WITHOUT CLUSTER";
6475 case AT_SetAccessMethod:
6476 return "SET ACCESS METHOD";
6477 case AT_SetLogged:
6478 return "SET LOGGED";
6479 case AT_SetUnLogged:
6480 return "SET UNLOGGED";
6481 case AT_DropOids:
6482 return "SET WITHOUT OIDS";
6483 case AT_SetTableSpace:
6484 return "SET TABLESPACE";
6485 case AT_SetRelOptions:
6486 return "SET";
6487 case AT_ResetRelOptions:
6488 return "RESET";
6489 case AT_ReplaceRelOptions:
6490 return NULL; /* not real grammar */
6491 case AT_EnableTrig:
6492 return "ENABLE TRIGGER";
6493 case AT_EnableAlwaysTrig:
6494 return "ENABLE ALWAYS TRIGGER";
6495 case AT_EnableReplicaTrig:
6496 return "ENABLE REPLICA TRIGGER";
6497 case AT_DisableTrig:
6498 return "DISABLE TRIGGER";
6499 case AT_EnableTrigAll:
6500 return "ENABLE TRIGGER ALL";
6501 case AT_DisableTrigAll:
6502 return "DISABLE TRIGGER ALL";
6503 case AT_EnableTrigUser:
6504 return "ENABLE TRIGGER USER";
6505 case AT_DisableTrigUser:
6506 return "DISABLE TRIGGER USER";
6507 case AT_EnableRule:
6508 return "ENABLE RULE";
6509 case AT_EnableAlwaysRule:
6510 return "ENABLE ALWAYS RULE";
6511 case AT_EnableReplicaRule:
6512 return "ENABLE REPLICA RULE";
6513 case AT_DisableRule:
6514 return "DISABLE RULE";
6515 case AT_AddInherit:
6516 return "INHERIT";
6517 case AT_DropInherit:
6518 return "NO INHERIT";
6519 case AT_AddOf:
6520 return "OF";
6521 case AT_DropOf:
6522 return "NOT OF";
6523 case AT_ReplicaIdentity:
6524 return "REPLICA IDENTITY";
6525 case AT_EnableRowSecurity:
6526 return "ENABLE ROW SECURITY";
6527 case AT_DisableRowSecurity:
6528 return "DISABLE ROW SECURITY";
6529 case AT_ForceRowSecurity:
6530 return "FORCE ROW SECURITY";
6531 case AT_NoForceRowSecurity:
6532 return "NO FORCE ROW SECURITY";
6533 case AT_GenericOptions:
6534 return "OPTIONS";
6535 case AT_AttachPartition:
6536 return "ATTACH PARTITION";
6537 case AT_DetachPartition:
6538 return "DETACH PARTITION";
6539 case AT_DetachPartitionFinalize:
6540 return "DETACH PARTITION ... FINALIZE";
6541 case AT_AddIdentity:
6542 return "ALTER COLUMN ... ADD IDENTITY";
6543 case AT_SetIdentity:
6544 return "ALTER COLUMN ... SET";
6545 case AT_DropIdentity:
6546 return "ALTER COLUMN ... DROP IDENTITY";
6547 case AT_ReAddStatistics:
6548 return NULL; /* not real grammar */
6551 return NULL;
6555 * ATSimplePermissions
6557 * - Ensure that it is a relation (or possibly a view)
6558 * - Ensure this user is the owner
6559 * - Ensure that it is not a system table
6561 static void
6562 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6564 int actual_target;
6566 switch (rel->rd_rel->relkind)
6568 case RELKIND_RELATION:
6569 case RELKIND_PARTITIONED_TABLE:
6570 actual_target = ATT_TABLE;
6571 break;
6572 case RELKIND_VIEW:
6573 actual_target = ATT_VIEW;
6574 break;
6575 case RELKIND_MATVIEW:
6576 actual_target = ATT_MATVIEW;
6577 break;
6578 case RELKIND_INDEX:
6579 actual_target = ATT_INDEX;
6580 break;
6581 case RELKIND_PARTITIONED_INDEX:
6582 actual_target = ATT_PARTITIONED_INDEX;
6583 break;
6584 case RELKIND_COMPOSITE_TYPE:
6585 actual_target = ATT_COMPOSITE_TYPE;
6586 break;
6587 case RELKIND_FOREIGN_TABLE:
6588 actual_target = ATT_FOREIGN_TABLE;
6589 break;
6590 case RELKIND_SEQUENCE:
6591 actual_target = ATT_SEQUENCE;
6592 break;
6593 default:
6594 actual_target = 0;
6595 break;
6598 /* Wrong target type? */
6599 if ((actual_target & allowed_targets) == 0)
6601 const char *action_str = alter_table_type_to_string(cmdtype);
6603 if (action_str)
6604 ereport(ERROR,
6605 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6606 /* translator: %s is a group of some SQL keywords */
6607 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6608 action_str, RelationGetRelationName(rel)),
6609 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6610 else
6611 /* internal error? */
6612 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6613 RelationGetRelationName(rel));
6616 /* Permissions checks */
6617 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6618 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6619 RelationGetRelationName(rel));
6621 if (!allowSystemTableMods && IsSystemRelation(rel))
6622 ereport(ERROR,
6623 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6624 errmsg("permission denied: \"%s\" is a system catalog",
6625 RelationGetRelationName(rel))));
6629 * ATSimpleRecursion
6631 * Simple table recursion sufficient for most ALTER TABLE operations.
6632 * All direct and indirect children are processed in an unspecified order.
6633 * Note that if a child inherits from the original table via multiple
6634 * inheritance paths, it will be visited just once.
6636 static void
6637 ATSimpleRecursion(List **wqueue, Relation rel,
6638 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6639 AlterTableUtilityContext *context)
6642 * Propagate to children, if desired and if there are (or might be) any
6643 * children.
6645 if (recurse && rel->rd_rel->relhassubclass)
6647 Oid relid = RelationGetRelid(rel);
6648 ListCell *child;
6649 List *children;
6651 children = find_all_inheritors(relid, lockmode, NULL);
6654 * find_all_inheritors does the recursive search of the inheritance
6655 * hierarchy, so all we have to do is process all of the relids in the
6656 * list that it returns.
6658 foreach(child, children)
6660 Oid childrelid = lfirst_oid(child);
6661 Relation childrel;
6663 if (childrelid == relid)
6664 continue;
6665 /* find_all_inheritors already got lock */
6666 childrel = relation_open(childrelid, NoLock);
6667 CheckTableNotInUse(childrel, "ALTER TABLE");
6668 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6669 relation_close(childrel, NoLock);
6675 * Obtain list of partitions of the given table, locking them all at the given
6676 * lockmode and ensuring that they all pass CheckTableNotInUse.
6678 * This function is a no-op if the given relation is not a partitioned table;
6679 * in particular, nothing is done if it's a legacy inheritance parent.
6681 static void
6682 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6684 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6686 List *inh;
6687 ListCell *cell;
6689 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6690 /* first element is the parent rel; must ignore it */
6691 for_each_from(cell, inh, 1)
6693 Relation childrel;
6695 /* find_all_inheritors already got lock */
6696 childrel = table_open(lfirst_oid(cell), NoLock);
6697 CheckTableNotInUse(childrel, "ALTER TABLE");
6698 table_close(childrel, NoLock);
6700 list_free(inh);
6705 * ATTypedTableRecursion
6707 * Propagate ALTER TYPE operations to the typed tables of that type.
6708 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6709 * recursion to inheritance children of the typed tables.
6711 static void
6712 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6713 LOCKMODE lockmode, AlterTableUtilityContext *context)
6715 ListCell *child;
6716 List *children;
6718 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6720 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6721 RelationGetRelationName(rel),
6722 cmd->behavior);
6724 foreach(child, children)
6726 Oid childrelid = lfirst_oid(child);
6727 Relation childrel;
6729 childrel = relation_open(childrelid, lockmode);
6730 CheckTableNotInUse(childrel, "ALTER TABLE");
6731 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6732 relation_close(childrel, NoLock);
6738 * find_composite_type_dependencies
6740 * Check to see if the type "typeOid" is being used as a column in some table
6741 * (possibly nested several levels deep in composite types, arrays, etc!).
6742 * Eventually, we'd like to propagate the check or rewrite operation
6743 * into such tables, but for now, just error out if we find any.
6745 * Caller should provide either the associated relation of a rowtype,
6746 * or a type name (not both) for use in the error message, if any.
6748 * Note that "typeOid" is not necessarily a composite type; it could also be
6749 * another container type such as an array or range, or a domain over one of
6750 * these things. The name of this function is therefore somewhat historical,
6751 * but it's not worth changing.
6753 * We assume that functions and views depending on the type are not reasons
6754 * to reject the ALTER. (How safe is this really?)
6756 void
6757 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6758 const char *origTypeName)
6760 Relation depRel;
6761 ScanKeyData key[2];
6762 SysScanDesc depScan;
6763 HeapTuple depTup;
6765 /* since this function recurses, it could be driven to stack overflow */
6766 check_stack_depth();
6769 * We scan pg_depend to find those things that depend on the given type.
6770 * (We assume we can ignore refobjsubid for a type.)
6772 depRel = table_open(DependRelationId, AccessShareLock);
6774 ScanKeyInit(&key[0],
6775 Anum_pg_depend_refclassid,
6776 BTEqualStrategyNumber, F_OIDEQ,
6777 ObjectIdGetDatum(TypeRelationId));
6778 ScanKeyInit(&key[1],
6779 Anum_pg_depend_refobjid,
6780 BTEqualStrategyNumber, F_OIDEQ,
6781 ObjectIdGetDatum(typeOid));
6783 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6784 NULL, 2, key);
6786 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6788 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6789 Relation rel;
6790 TupleDesc tupleDesc;
6791 Form_pg_attribute att;
6793 /* Check for directly dependent types */
6794 if (pg_depend->classid == TypeRelationId)
6797 * This must be an array, domain, or range containing the given
6798 * type, so recursively check for uses of this type. Note that
6799 * any error message will mention the original type not the
6800 * container; this is intentional.
6802 find_composite_type_dependencies(pg_depend->objid,
6803 origRelation, origTypeName);
6804 continue;
6807 /* Else, ignore dependees that aren't relations */
6808 if (pg_depend->classid != RelationRelationId)
6809 continue;
6811 rel = relation_open(pg_depend->objid, AccessShareLock);
6812 tupleDesc = RelationGetDescr(rel);
6815 * If objsubid identifies a specific column, refer to that in error
6816 * messages. Otherwise, search to see if there's a user column of the
6817 * type. (We assume system columns are never of interesting types.)
6818 * The search is needed because an index containing an expression
6819 * column of the target type will just be recorded as a whole-relation
6820 * dependency. If we do not find a column of the type, the dependency
6821 * must indicate that the type is transiently referenced in an index
6822 * expression but not stored on disk, which we assume is OK, just as
6823 * we do for references in views. (It could also be that the target
6824 * type is embedded in some container type that is stored in an index
6825 * column, but the previous recursion should catch such cases.)
6827 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6828 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6829 else
6831 att = NULL;
6832 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6834 att = TupleDescAttr(tupleDesc, attno - 1);
6835 if (att->atttypid == typeOid && !att->attisdropped)
6836 break;
6837 att = NULL;
6839 if (att == NULL)
6841 /* No such column, so assume OK */
6842 relation_close(rel, AccessShareLock);
6843 continue;
6848 * We definitely should reject if the relation has storage. If it's
6849 * partitioned, then perhaps we don't have to reject: if there are
6850 * partitions then we'll fail when we find one, else there is no
6851 * stored data to worry about. However, it's possible that the type
6852 * change would affect conclusions about whether the type is sortable
6853 * or hashable and thus (if it's a partitioning column) break the
6854 * partitioning rule. For now, reject for partitioned rels too.
6856 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6857 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6859 if (origTypeName)
6860 ereport(ERROR,
6861 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6862 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6863 origTypeName,
6864 RelationGetRelationName(rel),
6865 NameStr(att->attname))));
6866 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6867 ereport(ERROR,
6868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6869 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6870 RelationGetRelationName(origRelation),
6871 RelationGetRelationName(rel),
6872 NameStr(att->attname))));
6873 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6874 ereport(ERROR,
6875 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6876 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6877 RelationGetRelationName(origRelation),
6878 RelationGetRelationName(rel),
6879 NameStr(att->attname))));
6880 else
6881 ereport(ERROR,
6882 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6883 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6884 RelationGetRelationName(origRelation),
6885 RelationGetRelationName(rel),
6886 NameStr(att->attname))));
6888 else if (OidIsValid(rel->rd_rel->reltype))
6891 * A view or composite type itself isn't a problem, but we must
6892 * recursively check for indirect dependencies via its rowtype.
6894 find_composite_type_dependencies(rel->rd_rel->reltype,
6895 origRelation, origTypeName);
6898 relation_close(rel, AccessShareLock);
6901 systable_endscan(depScan);
6903 relation_close(depRel, AccessShareLock);
6908 * find_typed_table_dependencies
6910 * Check to see if a composite type is being used as the type of a
6911 * typed table. Abort if any are found and behavior is RESTRICT.
6912 * Else return the list of tables.
6914 static List *
6915 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6917 Relation classRel;
6918 ScanKeyData key[1];
6919 TableScanDesc scan;
6920 HeapTuple tuple;
6921 List *result = NIL;
6923 classRel = table_open(RelationRelationId, AccessShareLock);
6925 ScanKeyInit(&key[0],
6926 Anum_pg_class_reloftype,
6927 BTEqualStrategyNumber, F_OIDEQ,
6928 ObjectIdGetDatum(typeOid));
6930 scan = table_beginscan_catalog(classRel, 1, key);
6932 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6934 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6936 if (behavior == DROP_RESTRICT)
6937 ereport(ERROR,
6938 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6939 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6940 typeName),
6941 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6942 else
6943 result = lappend_oid(result, classform->oid);
6946 table_endscan(scan);
6947 table_close(classRel, AccessShareLock);
6949 return result;
6954 * check_of_type
6956 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6957 * isn't suitable, throw an error. Currently, we require that the type
6958 * originated with CREATE TYPE AS. We could support any row type, but doing so
6959 * would require handling a number of extra corner cases in the DDL commands.
6960 * (Also, allowing domain-over-composite would open up a can of worms about
6961 * whether and how the domain's constraints should apply to derived tables.)
6963 void
6964 check_of_type(HeapTuple typetuple)
6966 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6967 bool typeOk = false;
6969 if (typ->typtype == TYPTYPE_COMPOSITE)
6971 Relation typeRelation;
6973 Assert(OidIsValid(typ->typrelid));
6974 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6975 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6978 * Close the parent rel, but keep our AccessShareLock on it until xact
6979 * commit. That will prevent someone else from deleting or ALTERing
6980 * the type before the typed table creation/conversion commits.
6982 relation_close(typeRelation, NoLock);
6984 if (!typeOk)
6985 ereport(ERROR,
6986 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6987 errmsg("type %s is not a composite type",
6988 format_type_be(typ->oid))));
6993 * ALTER TABLE ADD COLUMN
6995 * Adds an additional attribute to a relation making the assumption that
6996 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6997 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6998 * AlterTableCmd's.
7000 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7001 * have to decide at runtime whether to recurse or not depending on whether we
7002 * actually add a column or merely merge with an existing column. (We can't
7003 * check this in a static pre-pass because it won't handle multiple inheritance
7004 * situations correctly.)
7006 static void
7007 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7008 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7009 AlterTableUtilityContext *context)
7011 if (rel->rd_rel->reloftype && !recursing)
7012 ereport(ERROR,
7013 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7014 errmsg("cannot add column to typed table")));
7016 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7017 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7019 if (recurse && !is_view)
7020 cmd->recurse = true;
7024 * Add a column to a table. The return value is the address of the
7025 * new column in the parent relation.
7027 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7028 * copy (but that happens only after we check for IF NOT EXISTS).
7030 static ObjectAddress
7031 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7032 AlterTableCmd **cmd, bool recurse, bool recursing,
7033 LOCKMODE lockmode, AlterTablePass cur_pass,
7034 AlterTableUtilityContext *context)
7036 Oid myrelid = RelationGetRelid(rel);
7037 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7038 bool if_not_exists = (*cmd)->missing_ok;
7039 Relation pgclass,
7040 attrdesc;
7041 HeapTuple reltup;
7042 Form_pg_attribute attribute;
7043 int newattnum;
7044 char relkind;
7045 Expr *defval;
7046 List *children;
7047 ListCell *child;
7048 AlterTableCmd *childcmd;
7049 ObjectAddress address;
7050 TupleDesc tupdesc;
7052 /* since this function recurses, it could be driven to stack overflow */
7053 check_stack_depth();
7055 /* At top level, permission check was done in ATPrepCmd, else do it */
7056 if (recursing)
7057 ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7059 if (rel->rd_rel->relispartition && !recursing)
7060 ereport(ERROR,
7061 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7062 errmsg("cannot add column to a partition")));
7064 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7067 * Are we adding the column to a recursion child? If so, check whether to
7068 * merge with an existing definition for the column. If we do merge, we
7069 * must not recurse. Children will already have the column, and recursing
7070 * into them would mess up attinhcount.
7072 if (colDef->inhcount > 0)
7074 HeapTuple tuple;
7076 /* Does child already have a column by this name? */
7077 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7078 if (HeapTupleIsValid(tuple))
7080 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7081 Oid ctypeId;
7082 int32 ctypmod;
7083 Oid ccollid;
7085 /* Child column must match on type, typmod, and collation */
7086 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7087 if (ctypeId != childatt->atttypid ||
7088 ctypmod != childatt->atttypmod)
7089 ereport(ERROR,
7090 (errcode(ERRCODE_DATATYPE_MISMATCH),
7091 errmsg("child table \"%s\" has different type for column \"%s\"",
7092 RelationGetRelationName(rel), colDef->colname)));
7093 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7094 if (ccollid != childatt->attcollation)
7095 ereport(ERROR,
7096 (errcode(ERRCODE_COLLATION_MISMATCH),
7097 errmsg("child table \"%s\" has different collation for column \"%s\"",
7098 RelationGetRelationName(rel), colDef->colname),
7099 errdetail("\"%s\" versus \"%s\"",
7100 get_collation_name(ccollid),
7101 get_collation_name(childatt->attcollation))));
7103 /* Bump the existing child att's inhcount */
7104 childatt->attinhcount++;
7105 if (childatt->attinhcount < 0)
7106 ereport(ERROR,
7107 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7108 errmsg("too many inheritance parents"));
7109 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7111 heap_freetuple(tuple);
7113 /* Inform the user about the merge */
7114 ereport(NOTICE,
7115 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7116 colDef->colname, RelationGetRelationName(rel))));
7118 table_close(attrdesc, RowExclusiveLock);
7120 /* Make the child column change visible */
7121 CommandCounterIncrement();
7123 return InvalidObjectAddress;
7127 /* skip if the name already exists and if_not_exists is true */
7128 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7130 table_close(attrdesc, RowExclusiveLock);
7131 return InvalidObjectAddress;
7135 * Okay, we need to add the column, so go ahead and do parse
7136 * transformation. This can result in queueing up, or even immediately
7137 * executing, subsidiary operations (such as creation of unique indexes);
7138 * so we mustn't do it until we have made the if_not_exists check.
7140 * When recursing, the command was already transformed and we needn't do
7141 * so again. Also, if context isn't given we can't transform. (That
7142 * currently happens only for AT_AddColumnToView; we expect that view.c
7143 * passed us a ColumnDef that doesn't need work.)
7145 if (context != NULL && !recursing)
7147 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7148 cur_pass, context);
7149 Assert(*cmd != NULL);
7150 colDef = castNode(ColumnDef, (*cmd)->def);
7154 * Regular inheritance children are independent enough not to inherit the
7155 * identity column from parent hence cannot recursively add identity
7156 * column if the table has inheritance children.
7158 * Partitions, on the other hand, are integral part of a partitioned table
7159 * and inherit identity column. Hence propagate identity column down the
7160 * partition hierarchy.
7162 if (colDef->identity &&
7163 recurse &&
7164 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7165 find_inheritance_children(myrelid, NoLock) != NIL)
7166 ereport(ERROR,
7167 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7168 errmsg("cannot recursively add identity column to table that has child tables")));
7170 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7172 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7173 if (!HeapTupleIsValid(reltup))
7174 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7175 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7177 /* Determine the new attribute's number */
7178 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7179 if (newattnum > MaxHeapAttributeNumber)
7180 ereport(ERROR,
7181 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7182 errmsg("tables can have at most %d columns",
7183 MaxHeapAttributeNumber)));
7186 * Construct new attribute's pg_attribute entry.
7188 tupdesc = BuildDescForRelation(list_make1(colDef));
7190 attribute = TupleDescAttr(tupdesc, 0);
7192 /* Fix up attribute number */
7193 attribute->attnum = newattnum;
7195 /* make sure datatype is legal for a column */
7196 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7197 list_make1_oid(rel->rd_rel->reltype),
7200 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7202 table_close(attrdesc, RowExclusiveLock);
7205 * Update pg_class tuple as appropriate
7207 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7209 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7211 heap_freetuple(reltup);
7213 /* Post creation hook for new attribute */
7214 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7216 table_close(pgclass, RowExclusiveLock);
7218 /* Make the attribute's catalog entry visible */
7219 CommandCounterIncrement();
7222 * Store the DEFAULT, if any, in the catalogs
7224 if (colDef->raw_default)
7226 RawColumnDefault *rawEnt;
7228 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7229 rawEnt->attnum = attribute->attnum;
7230 rawEnt->raw_default = copyObject(colDef->raw_default);
7233 * Attempt to skip a complete table rewrite by storing the specified
7234 * DEFAULT value outside of the heap. This may be disabled inside
7235 * AddRelationNewConstraints if the optimization cannot be applied.
7237 rawEnt->missingMode = (!colDef->generated);
7239 rawEnt->generated = colDef->generated;
7242 * This function is intended for CREATE TABLE, so it processes a
7243 * _list_ of defaults, but we just do one.
7245 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7246 false, true, false, NULL);
7248 /* Make the additional catalog changes visible */
7249 CommandCounterIncrement();
7252 * Did the request for a missing value work? If not we'll have to do a
7253 * rewrite
7255 if (!rawEnt->missingMode)
7256 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7260 * Tell Phase 3 to fill in the default expression, if there is one.
7262 * If there is no default, Phase 3 doesn't have to do anything, because
7263 * that effectively means that the default is NULL. The heap tuple access
7264 * routines always check for attnum > # of attributes in tuple, and return
7265 * NULL if so, so without any modification of the tuple data we will get
7266 * the effect of NULL values in the new column.
7268 * An exception occurs when the new column is of a domain type: the domain
7269 * might have a not-null constraint, or a check constraint that indirectly
7270 * rejects nulls. If there are any domain constraints then we construct
7271 * an explicit NULL default value that will be passed through
7272 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7273 * rewriting the table which we really don't have to do, but the present
7274 * design of domain processing doesn't offer any simple way of checking
7275 * the constraints more directly.)
7277 * Note: we use build_column_default, and not just the cooked default
7278 * returned by AddRelationNewConstraints, so that the right thing happens
7279 * when a datatype's default applies.
7281 * Note: it might seem that this should happen at the end of Phase 2, so
7282 * that the effects of subsequent subcommands can be taken into account.
7283 * It's intentional that we do it now, though. The new column should be
7284 * filled according to what is said in the ADD COLUMN subcommand, so that
7285 * the effects are the same as if this subcommand had been run by itself
7286 * and the later subcommands had been issued in new ALTER TABLE commands.
7288 * We can skip this entirely for relations without storage, since Phase 3
7289 * is certainly not going to touch them. System attributes don't have
7290 * interesting defaults, either.
7292 if (RELKIND_HAS_STORAGE(relkind))
7295 * For an identity column, we can't use build_column_default(),
7296 * because the sequence ownership isn't set yet. So do it manually.
7298 if (colDef->identity)
7300 NextValueExpr *nve = makeNode(NextValueExpr);
7302 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7303 nve->typeId = attribute->atttypid;
7305 defval = (Expr *) nve;
7307 /* must do a rewrite for identity columns */
7308 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7310 else
7311 defval = (Expr *) build_column_default(rel, attribute->attnum);
7313 if (!defval && DomainHasConstraints(attribute->atttypid))
7315 Oid baseTypeId;
7316 int32 baseTypeMod;
7317 Oid baseTypeColl;
7319 baseTypeMod = attribute->atttypmod;
7320 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7321 baseTypeColl = get_typcollation(baseTypeId);
7322 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7323 defval = (Expr *) coerce_to_target_type(NULL,
7324 (Node *) defval,
7325 baseTypeId,
7326 attribute->atttypid,
7327 attribute->atttypmod,
7328 COERCION_ASSIGNMENT,
7329 COERCE_IMPLICIT_CAST,
7330 -1);
7331 if (defval == NULL) /* should not happen */
7332 elog(ERROR, "failed to coerce base type to domain");
7335 if (defval)
7337 NewColumnValue *newval;
7339 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7340 newval->attnum = attribute->attnum;
7341 newval->expr = expression_planner(defval);
7342 newval->is_generated = (colDef->generated != '\0');
7344 tab->newvals = lappend(tab->newvals, newval);
7347 if (DomainHasConstraints(attribute->atttypid))
7348 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7350 if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7353 * If the new column is NOT NULL, and there is no missing value,
7354 * tell Phase 3 it needs to check for NULLs.
7356 tab->verify_new_notnull |= colDef->is_not_null;
7361 * Add needed dependency entries for the new column.
7363 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7364 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7367 * Propagate to children as appropriate. Unlike most other ALTER
7368 * routines, we have to do this one level of recursion at a time; we can't
7369 * use find_all_inheritors to do it in one pass.
7371 children =
7372 find_inheritance_children(RelationGetRelid(rel), lockmode);
7375 * If we are told not to recurse, there had better not be any child
7376 * tables; else the addition would put them out of step.
7378 if (children && !recurse)
7379 ereport(ERROR,
7380 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7381 errmsg("column must be added to child tables too")));
7383 /* Children should see column as singly inherited */
7384 if (!recursing)
7386 childcmd = copyObject(*cmd);
7387 colDef = castNode(ColumnDef, childcmd->def);
7388 colDef->inhcount = 1;
7389 colDef->is_local = false;
7391 else
7392 childcmd = *cmd; /* no need to copy again */
7394 foreach(child, children)
7396 Oid childrelid = lfirst_oid(child);
7397 Relation childrel;
7398 AlteredTableInfo *childtab;
7400 /* find_inheritance_children already got lock */
7401 childrel = table_open(childrelid, NoLock);
7402 CheckTableNotInUse(childrel, "ALTER TABLE");
7404 /* Find or create work queue entry for this table */
7405 childtab = ATGetQueueEntry(wqueue, childrel);
7407 /* Recurse to child; return value is ignored */
7408 ATExecAddColumn(wqueue, childtab, childrel,
7409 &childcmd, recurse, true,
7410 lockmode, cur_pass, context);
7412 table_close(childrel, NoLock);
7415 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7416 return address;
7420 * If a new or renamed column will collide with the name of an existing
7421 * column and if_not_exists is false then error out, else do nothing.
7423 static bool
7424 check_for_column_name_collision(Relation rel, const char *colname,
7425 bool if_not_exists)
7427 HeapTuple attTuple;
7428 int attnum;
7431 * this test is deliberately not attisdropped-aware, since if one tries to
7432 * add a column matching a dropped column name, it's gonna fail anyway.
7434 attTuple = SearchSysCache2(ATTNAME,
7435 ObjectIdGetDatum(RelationGetRelid(rel)),
7436 PointerGetDatum(colname));
7437 if (!HeapTupleIsValid(attTuple))
7438 return true;
7440 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7441 ReleaseSysCache(attTuple);
7444 * We throw a different error message for conflicts with system column
7445 * names, since they are normally not shown and the user might otherwise
7446 * be confused about the reason for the conflict.
7448 if (attnum <= 0)
7449 ereport(ERROR,
7450 (errcode(ERRCODE_DUPLICATE_COLUMN),
7451 errmsg("column name \"%s\" conflicts with a system column name",
7452 colname)));
7453 else
7455 if (if_not_exists)
7457 ereport(NOTICE,
7458 (errcode(ERRCODE_DUPLICATE_COLUMN),
7459 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7460 colname, RelationGetRelationName(rel))));
7461 return false;
7464 ereport(ERROR,
7465 (errcode(ERRCODE_DUPLICATE_COLUMN),
7466 errmsg("column \"%s\" of relation \"%s\" already exists",
7467 colname, RelationGetRelationName(rel))));
7470 return true;
7474 * Install a column's dependency on its datatype.
7476 static void
7477 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7479 ObjectAddress myself,
7480 referenced;
7482 myself.classId = RelationRelationId;
7483 myself.objectId = relid;
7484 myself.objectSubId = attnum;
7485 referenced.classId = TypeRelationId;
7486 referenced.objectId = typid;
7487 referenced.objectSubId = 0;
7488 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7492 * Install a column's dependency on its collation.
7494 static void
7495 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7497 ObjectAddress myself,
7498 referenced;
7500 /* We know the default collation is pinned, so don't bother recording it */
7501 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7503 myself.classId = RelationRelationId;
7504 myself.objectId = relid;
7505 myself.objectSubId = attnum;
7506 referenced.classId = CollationRelationId;
7507 referenced.objectId = collid;
7508 referenced.objectSubId = 0;
7509 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7514 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7516 * Return the address of the modified column. If the column was already
7517 * nullable, InvalidObjectAddress is returned.
7519 static ObjectAddress
7520 ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7521 LOCKMODE lockmode)
7523 HeapTuple tuple;
7524 HeapTuple conTup;
7525 Form_pg_attribute attTup;
7526 AttrNumber attnum;
7527 Relation attr_rel;
7528 ObjectAddress address;
7529 List *readyRels;
7532 * lookup the attribute
7534 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7536 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7537 if (!HeapTupleIsValid(tuple))
7538 ereport(ERROR,
7539 (errcode(ERRCODE_UNDEFINED_COLUMN),
7540 errmsg("column \"%s\" of relation \"%s\" does not exist",
7541 colName, RelationGetRelationName(rel))));
7542 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7543 attnum = attTup->attnum;
7544 ObjectAddressSubSet(address, RelationRelationId,
7545 RelationGetRelid(rel), attnum);
7547 /* If the column is already nullable there's nothing to do. */
7548 if (!attTup->attnotnull)
7550 table_close(attr_rel, RowExclusiveLock);
7551 return InvalidObjectAddress;
7554 /* Prevent them from altering a system attribute */
7555 if (attnum <= 0)
7556 ereport(ERROR,
7557 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7558 errmsg("cannot alter system column \"%s\"",
7559 colName)));
7561 if (attTup->attidentity)
7562 ereport(ERROR,
7563 (errcode(ERRCODE_SYNTAX_ERROR),
7564 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7565 colName, RelationGetRelationName(rel))));
7568 * It's not OK to remove a constraint only for the parent and leave it in
7569 * the children, so disallow that.
7571 if (!recurse)
7573 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7575 PartitionDesc partdesc;
7577 partdesc = RelationGetPartitionDesc(rel, true);
7579 if (partdesc->nparts > 0)
7580 ereport(ERROR,
7581 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7582 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7583 errhint("Do not specify the ONLY keyword."));
7585 else if (rel->rd_rel->relhassubclass &&
7586 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7588 ereport(ERROR,
7589 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7590 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7591 colName),
7592 errhint("Do not specify the ONLY keyword."));
7597 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7599 if (rel->rd_rel->relispartition)
7601 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7602 Relation parent = table_open(parentId, AccessShareLock);
7603 TupleDesc tupDesc = RelationGetDescr(parent);
7604 AttrNumber parent_attnum;
7606 parent_attnum = get_attnum(parentId, colName);
7607 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7608 ereport(ERROR,
7609 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7610 errmsg("column \"%s\" is marked NOT NULL in parent table",
7611 colName)));
7612 table_close(parent, AccessShareLock);
7616 * Find the constraint that makes this column NOT NULL.
7618 conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7619 if (conTup == NULL)
7621 Bitmapset *pkcols;
7624 * There's no not-null constraint, so throw an error. If the column
7625 * is in a primary key, we can throw a specific error. Otherwise,
7626 * this is unexpected.
7628 pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7629 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7630 pkcols))
7631 ereport(ERROR,
7632 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7633 errmsg("column \"%s\" is in a primary key", colName));
7635 /* this shouldn't happen */
7636 elog(ERROR, "could not find not-null constraint on column \"%s\", relation \"%s\"",
7637 colName, RelationGetRelationName(rel));
7640 readyRels = NIL;
7641 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7642 false, &readyRels, lockmode);
7644 heap_freetuple(conTup);
7646 InvokeObjectPostAlterHook(RelationRelationId,
7647 RelationGetRelid(rel), attnum);
7649 table_close(attr_rel, RowExclusiveLock);
7651 return address;
7655 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7656 * to verify it; recurses to apply the same to children.
7658 * When called to alter an existing table, 'wqueue' must be given so that we can
7659 * queue a check that existing tuples pass the constraint. When called from
7660 * table creation, 'wqueue' should be passed as NULL.
7662 * Returns true if the flag was set in any table, otherwise false.
7664 static bool
7665 set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7666 LOCKMODE lockmode)
7668 HeapTuple tuple;
7669 Form_pg_attribute attForm;
7670 bool retval = false;
7672 /* Guard against stack overflow due to overly deep inheritance tree. */
7673 check_stack_depth();
7675 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7676 if (!HeapTupleIsValid(tuple))
7677 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7678 attnum, RelationGetRelid(rel));
7679 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7680 if (!attForm->attnotnull)
7682 Relation attr_rel;
7684 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7686 attForm->attnotnull = true;
7687 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7689 table_close(attr_rel, RowExclusiveLock);
7692 * And set up for existing values to be checked, unless another
7693 * constraint already proves this.
7695 if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7697 AlteredTableInfo *tab;
7699 tab = ATGetQueueEntry(wqueue, rel);
7700 tab->verify_new_notnull = true;
7703 retval = true;
7706 if (recurse)
7708 List *children;
7709 ListCell *lc;
7711 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7712 foreach(lc, children)
7714 Oid childrelid = lfirst_oid(lc);
7715 Relation childrel;
7716 AttrNumber childattno;
7718 /* find_inheritance_children already got lock */
7719 childrel = table_open(childrelid, NoLock);
7720 CheckTableNotInUse(childrel, "ALTER TABLE");
7722 childattno = get_attnum(RelationGetRelid(childrel),
7723 get_attname(RelationGetRelid(rel), attnum,
7724 false));
7725 retval |= set_attnotnull(wqueue, childrel, childattno,
7726 recurse, lockmode);
7727 table_close(childrel, NoLock);
7731 return retval;
7735 * ALTER TABLE ALTER COLUMN SET NOT NULL
7737 * Add a not-null constraint to a single table and its children. Returns
7738 * the address of the constraint added to the parent relation, if one gets
7739 * added, or InvalidObjectAddress otherwise.
7741 * We must recurse to child tables during execution, rather than using
7742 * ALTER TABLE's normal prep-time recursion.
7744 static ObjectAddress
7745 ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7746 bool recurse, bool recursing, List **readyRels,
7747 LOCKMODE lockmode)
7749 HeapTuple tuple;
7750 Relation constr_rel;
7751 ScanKeyData skey;
7752 SysScanDesc conscan;
7753 AttrNumber attnum;
7754 ObjectAddress address;
7755 Constraint *constraint;
7756 CookedConstraint *ccon;
7757 List *cooked;
7758 bool is_no_inherit = false;
7759 List *ready = NIL;
7761 /* Guard against stack overflow due to overly deep inheritance tree. */
7762 check_stack_depth();
7765 * In cases of multiple inheritance, we might visit the same child more
7766 * than once. In the topmost call, set up a list that we fill with all
7767 * visited relations, to skip those.
7769 if (readyRels == NULL)
7771 Assert(!recursing);
7772 readyRels = &ready;
7774 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7775 return InvalidObjectAddress;
7776 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7778 /* At top level, permission check was done in ATPrepCmd, else do it */
7779 if (recursing)
7781 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7782 Assert(conName != NULL);
7785 attnum = get_attnum(RelationGetRelid(rel), colName);
7786 if (attnum == InvalidAttrNumber)
7787 ereport(ERROR,
7788 (errcode(ERRCODE_UNDEFINED_COLUMN),
7789 errmsg("column \"%s\" of relation \"%s\" does not exist",
7790 colName, RelationGetRelationName(rel))));
7792 /* Prevent them from altering a system attribute */
7793 if (attnum <= 0)
7794 ereport(ERROR,
7795 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7796 errmsg("cannot alter system column \"%s\"",
7797 colName)));
7799 /* See if there's already a constraint */
7800 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7801 ScanKeyInit(&skey,
7802 Anum_pg_constraint_conrelid,
7803 BTEqualStrategyNumber, F_OIDEQ,
7804 ObjectIdGetDatum(RelationGetRelid(rel)));
7805 conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7806 NULL, 1, &skey);
7808 while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7810 Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7811 bool changed = false;
7812 HeapTuple copytup;
7814 if (conForm->contype != CONSTRAINT_NOTNULL)
7815 continue;
7817 if (extractNotNullColumn(tuple) != attnum)
7818 continue;
7820 copytup = heap_copytuple(tuple);
7821 conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7824 * If we find an appropriate constraint, we're almost done, but just
7825 * need to change some properties on it: if we're recursing, increment
7826 * coninhcount; if not, set conislocal if not already set.
7828 if (recursing)
7830 conForm->coninhcount++;
7831 changed = true;
7833 else if (!conForm->conislocal)
7835 conForm->conislocal = true;
7836 changed = true;
7839 if (changed)
7841 CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
7842 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7845 systable_endscan(conscan);
7846 table_close(constr_rel, RowExclusiveLock);
7848 if (changed)
7849 return address;
7850 else
7851 return InvalidObjectAddress;
7854 systable_endscan(conscan);
7855 table_close(constr_rel, RowExclusiveLock);
7858 * If we're asked not to recurse, and children exist, raise an error for
7859 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7860 * specified.
7862 if (!recurse &&
7863 find_inheritance_children(RelationGetRelid(rel),
7864 NoLock) != NIL)
7866 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7867 ereport(ERROR,
7868 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7869 errmsg("constraint must be added to child tables too"),
7870 errhint("Do not specify the ONLY keyword."));
7871 else
7872 is_no_inherit = true;
7876 * No constraint exists; we must add one. First determine a name to use,
7877 * if we haven't already.
7879 if (!recursing)
7881 Assert(conName == NULL);
7882 conName = ChooseConstraintName(RelationGetRelationName(rel),
7883 colName, "not_null",
7884 RelationGetNamespace(rel),
7885 NIL);
7887 constraint = makeNode(Constraint);
7888 constraint->contype = CONSTR_NOTNULL;
7889 constraint->conname = conName;
7890 constraint->deferrable = false;
7891 constraint->initdeferred = false;
7892 constraint->location = -1;
7893 constraint->keys = list_make1(makeString(colName));
7894 constraint->is_no_inherit = is_no_inherit;
7895 constraint->inhcount = recursing ? 1 : 0;
7896 constraint->skip_validation = false;
7897 constraint->initially_valid = true;
7899 /* and do it */
7900 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7901 false, !recursing, false, NULL);
7902 ccon = linitial(cooked);
7903 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7905 InvokeObjectPostAlterHook(RelationRelationId,
7906 RelationGetRelid(rel), attnum);
7909 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7910 * recurse, because we're going to do it here.
7912 set_attnotnull(wqueue, rel, attnum, false, lockmode);
7915 * Recurse to propagate the constraint to children that don't have one.
7917 if (recurse)
7919 List *children;
7920 ListCell *lc;
7922 children = find_inheritance_children(RelationGetRelid(rel),
7923 lockmode);
7925 foreach(lc, children)
7927 Relation childrel;
7929 childrel = table_open(lfirst_oid(lc), NoLock);
7931 ATExecSetNotNull(wqueue, childrel,
7932 conName, colName, recurse, true,
7933 readyRels, lockmode);
7935 table_close(childrel, NoLock);
7939 return address;
7943 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
7945 * This doesn't exist in the grammar; it's used when creating a
7946 * primary key and the column is not already marked attnotnull.
7948 static ObjectAddress
7949 ATExecSetAttNotNull(List **wqueue, Relation rel,
7950 const char *colName, LOCKMODE lockmode)
7952 AttrNumber attnum;
7953 ObjectAddress address = InvalidObjectAddress;
7955 attnum = get_attnum(RelationGetRelid(rel), colName);
7956 if (attnum == InvalidAttrNumber)
7957 ereport(ERROR,
7958 errcode(ERRCODE_UNDEFINED_COLUMN),
7959 errmsg("column \"%s\" of relation \"%s\" does not exist",
7960 colName, RelationGetRelationName(rel)));
7963 * Make the change, if necessary, and only if so report the column as
7964 * changed
7966 if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
7967 ObjectAddressSubSet(address, RelationRelationId,
7968 RelationGetRelid(rel), attnum);
7970 return address;
7974 * NotNullImpliedByRelConstraints
7975 * Does rel's existing constraints imply NOT NULL for the given attribute?
7977 static bool
7978 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7980 NullTest *nnulltest = makeNode(NullTest);
7982 nnulltest->arg = (Expr *) makeVar(1,
7983 attr->attnum,
7984 attr->atttypid,
7985 attr->atttypmod,
7986 attr->attcollation,
7988 nnulltest->nulltesttype = IS_NOT_NULL;
7991 * argisrow = false is correct even for a composite column, because
7992 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7993 * case, just IS DISTINCT FROM NULL.
7995 nnulltest->argisrow = false;
7996 nnulltest->location = -1;
7998 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8000 ereport(DEBUG1,
8001 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8002 RelationGetRelationName(rel), NameStr(attr->attname))));
8003 return true;
8006 return false;
8010 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8012 * Return the address of the affected column.
8014 static ObjectAddress
8015 ATExecColumnDefault(Relation rel, const char *colName,
8016 Node *newDefault, LOCKMODE lockmode)
8018 TupleDesc tupdesc = RelationGetDescr(rel);
8019 AttrNumber attnum;
8020 ObjectAddress address;
8023 * get the number of the attribute
8025 attnum = get_attnum(RelationGetRelid(rel), colName);
8026 if (attnum == InvalidAttrNumber)
8027 ereport(ERROR,
8028 (errcode(ERRCODE_UNDEFINED_COLUMN),
8029 errmsg("column \"%s\" of relation \"%s\" does not exist",
8030 colName, RelationGetRelationName(rel))));
8032 /* Prevent them from altering a system attribute */
8033 if (attnum <= 0)
8034 ereport(ERROR,
8035 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8036 errmsg("cannot alter system column \"%s\"",
8037 colName)));
8039 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8040 ereport(ERROR,
8041 (errcode(ERRCODE_SYNTAX_ERROR),
8042 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8043 colName, RelationGetRelationName(rel)),
8044 /* translator: %s is an SQL ALTER command */
8045 newDefault ? 0 : errhint("Use %s instead.",
8046 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8048 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8049 ereport(ERROR,
8050 (errcode(ERRCODE_SYNTAX_ERROR),
8051 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8052 colName, RelationGetRelationName(rel)),
8053 newDefault ?
8054 /* translator: %s is an SQL ALTER command */
8055 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8056 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8057 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8060 * Remove any old default for the column. We use RESTRICT here for
8061 * safety, but at present we do not expect anything to depend on the
8062 * default.
8064 * We treat removing the existing default as an internal operation when it
8065 * is preparatory to adding a new default, but as a user-initiated
8066 * operation when the user asked for a drop.
8068 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8069 newDefault != NULL);
8071 if (newDefault)
8073 /* SET DEFAULT */
8074 RawColumnDefault *rawEnt;
8076 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8077 rawEnt->attnum = attnum;
8078 rawEnt->raw_default = newDefault;
8079 rawEnt->missingMode = false;
8080 rawEnt->generated = '\0';
8083 * This function is intended for CREATE TABLE, so it processes a
8084 * _list_ of defaults, but we just do one.
8086 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8087 false, true, false, NULL);
8090 ObjectAddressSubSet(address, RelationRelationId,
8091 RelationGetRelid(rel), attnum);
8092 return address;
8096 * Add a pre-cooked default expression.
8098 * Return the address of the affected column.
8100 static ObjectAddress
8101 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8102 Node *newDefault)
8104 ObjectAddress address;
8106 /* We assume no checking is required */
8109 * Remove any old default for the column. We use RESTRICT here for
8110 * safety, but at present we do not expect anything to depend on the
8111 * default. (In ordinary cases, there could not be a default in place
8112 * anyway, but it's possible when combining LIKE with inheritance.)
8114 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8115 true);
8117 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8119 ObjectAddressSubSet(address, RelationRelationId,
8120 RelationGetRelid(rel), attnum);
8121 return address;
8125 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8127 * Return the address of the affected column.
8129 static ObjectAddress
8130 ATExecAddIdentity(Relation rel, const char *colName,
8131 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8133 Relation attrelation;
8134 HeapTuple tuple;
8135 Form_pg_attribute attTup;
8136 AttrNumber attnum;
8137 ObjectAddress address;
8138 ColumnDef *cdef = castNode(ColumnDef, def);
8139 bool ispartitioned;
8141 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8142 if (ispartitioned && !recurse)
8143 ereport(ERROR,
8144 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8145 errmsg("cannot add identity to a column of only the partitioned table"),
8146 errhint("Do not specify the ONLY keyword.")));
8148 if (rel->rd_rel->relispartition && !recursing)
8149 ereport(ERROR,
8150 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8151 errmsg("cannot add identity to a column of a partition"));
8153 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8155 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8156 if (!HeapTupleIsValid(tuple))
8157 ereport(ERROR,
8158 (errcode(ERRCODE_UNDEFINED_COLUMN),
8159 errmsg("column \"%s\" of relation \"%s\" does not exist",
8160 colName, RelationGetRelationName(rel))));
8161 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8162 attnum = attTup->attnum;
8164 /* Can't alter a system attribute */
8165 if (attnum <= 0)
8166 ereport(ERROR,
8167 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8168 errmsg("cannot alter system column \"%s\"",
8169 colName)));
8172 * Creating a column as identity implies NOT NULL, so adding the identity
8173 * to an existing column that is not NOT NULL would create a state that
8174 * cannot be reproduced without contortions.
8176 if (!attTup->attnotnull)
8177 ereport(ERROR,
8178 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8179 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8180 colName, RelationGetRelationName(rel))));
8182 if (attTup->attidentity)
8183 ereport(ERROR,
8184 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8185 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8186 colName, RelationGetRelationName(rel))));
8188 if (attTup->atthasdef)
8189 ereport(ERROR,
8190 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8191 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8192 colName, RelationGetRelationName(rel))));
8194 attTup->attidentity = cdef->identity;
8195 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8197 InvokeObjectPostAlterHook(RelationRelationId,
8198 RelationGetRelid(rel),
8199 attTup->attnum);
8200 ObjectAddressSubSet(address, RelationRelationId,
8201 RelationGetRelid(rel), attnum);
8202 heap_freetuple(tuple);
8204 table_close(attrelation, RowExclusiveLock);
8207 * Recurse to propagate the identity column to partitions. Identity is
8208 * not inherited in regular inheritance children.
8210 if (recurse && ispartitioned)
8212 List *children;
8213 ListCell *lc;
8215 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8217 foreach(lc, children)
8219 Relation childrel;
8221 childrel = table_open(lfirst_oid(lc), NoLock);
8222 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8223 table_close(childrel, NoLock);
8227 return address;
8231 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8233 * Return the address of the affected column.
8235 static ObjectAddress
8236 ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8237 LOCKMODE lockmode, bool recurse, bool recursing)
8239 ListCell *option;
8240 DefElem *generatedEl = NULL;
8241 HeapTuple tuple;
8242 Form_pg_attribute attTup;
8243 AttrNumber attnum;
8244 Relation attrelation;
8245 ObjectAddress address;
8246 bool ispartitioned;
8248 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8249 if (ispartitioned && !recurse)
8250 ereport(ERROR,
8251 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8252 errmsg("cannot change identity column of only the partitioned table"),
8253 errhint("Do not specify the ONLY keyword.")));
8255 if (rel->rd_rel->relispartition && !recursing)
8256 ereport(ERROR,
8257 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8258 errmsg("cannot change identity column of a partition"));
8260 foreach(option, castNode(List, def))
8262 DefElem *defel = lfirst_node(DefElem, option);
8264 if (strcmp(defel->defname, "generated") == 0)
8266 if (generatedEl)
8267 ereport(ERROR,
8268 (errcode(ERRCODE_SYNTAX_ERROR),
8269 errmsg("conflicting or redundant options")));
8270 generatedEl = defel;
8272 else
8273 elog(ERROR, "option \"%s\" not recognized",
8274 defel->defname);
8278 * Even if there is nothing to change here, we run all the checks. There
8279 * will be a subsequent ALTER SEQUENCE that relies on everything being
8280 * there.
8283 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8284 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8285 if (!HeapTupleIsValid(tuple))
8286 ereport(ERROR,
8287 (errcode(ERRCODE_UNDEFINED_COLUMN),
8288 errmsg("column \"%s\" of relation \"%s\" does not exist",
8289 colName, RelationGetRelationName(rel))));
8291 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8292 attnum = attTup->attnum;
8294 if (attnum <= 0)
8295 ereport(ERROR,
8296 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8297 errmsg("cannot alter system column \"%s\"",
8298 colName)));
8300 if (!attTup->attidentity)
8301 ereport(ERROR,
8302 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8303 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8304 colName, RelationGetRelationName(rel))));
8306 if (generatedEl)
8308 attTup->attidentity = defGetInt32(generatedEl);
8309 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8311 InvokeObjectPostAlterHook(RelationRelationId,
8312 RelationGetRelid(rel),
8313 attTup->attnum);
8314 ObjectAddressSubSet(address, RelationRelationId,
8315 RelationGetRelid(rel), attnum);
8317 else
8318 address = InvalidObjectAddress;
8320 heap_freetuple(tuple);
8321 table_close(attrelation, RowExclusiveLock);
8324 * Recurse to propagate the identity change to partitions. Identity is not
8325 * inherited in regular inheritance children.
8327 if (generatedEl && recurse && ispartitioned)
8329 List *children;
8330 ListCell *lc;
8332 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8334 foreach(lc, children)
8336 Relation childrel;
8338 childrel = table_open(lfirst_oid(lc), NoLock);
8339 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8340 table_close(childrel, NoLock);
8344 return address;
8348 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8350 * Return the address of the affected column.
8352 static ObjectAddress
8353 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8354 bool recurse, bool recursing)
8356 HeapTuple tuple;
8357 Form_pg_attribute attTup;
8358 AttrNumber attnum;
8359 Relation attrelation;
8360 ObjectAddress address;
8361 Oid seqid;
8362 ObjectAddress seqaddress;
8363 bool ispartitioned;
8365 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8366 if (ispartitioned && !recurse)
8367 ereport(ERROR,
8368 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8369 errmsg("cannot drop identity from a column of only the partitioned table"),
8370 errhint("Do not specify the ONLY keyword.")));
8372 if (rel->rd_rel->relispartition && !recursing)
8373 ereport(ERROR,
8374 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8375 errmsg("cannot drop identity from a column of a partition"));
8377 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8378 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8379 if (!HeapTupleIsValid(tuple))
8380 ereport(ERROR,
8381 (errcode(ERRCODE_UNDEFINED_COLUMN),
8382 errmsg("column \"%s\" of relation \"%s\" does not exist",
8383 colName, RelationGetRelationName(rel))));
8385 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8386 attnum = attTup->attnum;
8388 if (attnum <= 0)
8389 ereport(ERROR,
8390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8391 errmsg("cannot alter system column \"%s\"",
8392 colName)));
8394 if (!attTup->attidentity)
8396 if (!missing_ok)
8397 ereport(ERROR,
8398 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8399 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8400 colName, RelationGetRelationName(rel))));
8401 else
8403 ereport(NOTICE,
8404 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8405 colName, RelationGetRelationName(rel))));
8406 heap_freetuple(tuple);
8407 table_close(attrelation, RowExclusiveLock);
8408 return InvalidObjectAddress;
8412 attTup->attidentity = '\0';
8413 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8415 InvokeObjectPostAlterHook(RelationRelationId,
8416 RelationGetRelid(rel),
8417 attTup->attnum);
8418 ObjectAddressSubSet(address, RelationRelationId,
8419 RelationGetRelid(rel), attnum);
8420 heap_freetuple(tuple);
8422 table_close(attrelation, RowExclusiveLock);
8425 * Recurse to drop the identity from column in partitions. Identity is
8426 * not inherited in regular inheritance children so ignore them.
8428 if (recurse && ispartitioned)
8430 List *children;
8431 ListCell *lc;
8433 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8435 foreach(lc, children)
8437 Relation childrel;
8439 childrel = table_open(lfirst_oid(lc), NoLock);
8440 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8441 table_close(childrel, NoLock);
8445 if (!recursing)
8447 /* drop the internal sequence */
8448 seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
8449 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8450 RelationRelationId, DEPENDENCY_INTERNAL);
8451 CommandCounterIncrement();
8452 seqaddress.classId = RelationRelationId;
8453 seqaddress.objectId = seqid;
8454 seqaddress.objectSubId = 0;
8455 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8458 return address;
8462 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8464 * Return the address of the affected column.
8466 static ObjectAddress
8467 ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8468 Node *newExpr, LOCKMODE lockmode)
8470 HeapTuple tuple;
8471 Form_pg_attribute attTup;
8472 AttrNumber attnum;
8473 Oid attrdefoid;
8474 ObjectAddress address;
8475 Expr *defval;
8476 NewColumnValue *newval;
8477 RawColumnDefault *rawEnt;
8479 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8480 if (!HeapTupleIsValid(tuple))
8481 ereport(ERROR,
8482 (errcode(ERRCODE_UNDEFINED_COLUMN),
8483 errmsg("column \"%s\" of relation \"%s\" does not exist",
8484 colName, RelationGetRelationName(rel))));
8486 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8487 attnum = attTup->attnum;
8489 if (attnum <= 0)
8490 ereport(ERROR,
8491 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8492 errmsg("cannot alter system column \"%s\"",
8493 colName)));
8495 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8496 ereport(ERROR,
8497 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8498 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8499 colName, RelationGetRelationName(rel))));
8500 ReleaseSysCache(tuple);
8503 * Clear all the missing values if we're rewriting the table, since this
8504 * renders them pointless.
8506 RelationClearMissing(rel);
8508 /* make sure we don't conflict with later attribute modifications */
8509 CommandCounterIncrement();
8512 * Find everything that depends on the column (constraints, indexes, etc),
8513 * and record enough information to let us recreate the objects after
8514 * rewrite.
8516 RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8519 * Drop the dependency records of the GENERATED expression, in particular
8520 * its INTERNAL dependency on the column, which would otherwise cause
8521 * dependency.c to refuse to perform the deletion.
8523 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8524 if (!OidIsValid(attrdefoid))
8525 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8526 RelationGetRelid(rel), attnum);
8527 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8529 /* Make above changes visible */
8530 CommandCounterIncrement();
8533 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8534 * safety, but at present we do not expect anything to depend on the
8535 * expression.
8537 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8538 false, false);
8540 /* Prepare to store the new expression, in the catalogs */
8541 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8542 rawEnt->attnum = attnum;
8543 rawEnt->raw_default = newExpr;
8544 rawEnt->missingMode = false;
8545 rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8547 /* Store the generated expression */
8548 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8549 false, true, false, NULL);
8551 /* Make above new expression visible */
8552 CommandCounterIncrement();
8554 /* Prepare for table rewrite */
8555 defval = (Expr *) build_column_default(rel, attnum);
8557 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8558 newval->attnum = attnum;
8559 newval->expr = expression_planner(defval);
8560 newval->is_generated = true;
8562 tab->newvals = lappend(tab->newvals, newval);
8563 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8565 /* Drop any pg_statistic entry for the column */
8566 RemoveStatistics(RelationGetRelid(rel), attnum);
8568 InvokeObjectPostAlterHook(RelationRelationId,
8569 RelationGetRelid(rel), attnum);
8571 ObjectAddressSubSet(address, RelationRelationId,
8572 RelationGetRelid(rel), attnum);
8573 return address;
8577 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8579 static void
8580 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8583 * Reject ONLY if there are child tables. We could implement this, but it
8584 * is a bit complicated. GENERATED clauses must be attached to the column
8585 * definition and cannot be added later like DEFAULT, so if a child table
8586 * has a generation expression that the parent does not have, the child
8587 * column will necessarily be an attislocal column. So to implement ONLY
8588 * here, we'd need extra code to update attislocal of the direct child
8589 * tables, somewhat similar to how DROP COLUMN does it, so that the
8590 * resulting state can be properly dumped and restored.
8592 if (!recurse &&
8593 find_inheritance_children(RelationGetRelid(rel), lockmode))
8594 ereport(ERROR,
8595 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8596 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8599 * Cannot drop generation expression from inherited columns.
8601 if (!recursing)
8603 HeapTuple tuple;
8604 Form_pg_attribute attTup;
8606 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8607 if (!HeapTupleIsValid(tuple))
8608 ereport(ERROR,
8609 (errcode(ERRCODE_UNDEFINED_COLUMN),
8610 errmsg("column \"%s\" of relation \"%s\" does not exist",
8611 cmd->name, RelationGetRelationName(rel))));
8613 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8615 if (attTup->attinhcount > 0)
8616 ereport(ERROR,
8617 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8618 errmsg("cannot drop generation expression from inherited column")));
8623 * Return the address of the affected column.
8625 static ObjectAddress
8626 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8628 HeapTuple tuple;
8629 Form_pg_attribute attTup;
8630 AttrNumber attnum;
8631 Relation attrelation;
8632 Oid attrdefoid;
8633 ObjectAddress address;
8635 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8636 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8637 if (!HeapTupleIsValid(tuple))
8638 ereport(ERROR,
8639 (errcode(ERRCODE_UNDEFINED_COLUMN),
8640 errmsg("column \"%s\" of relation \"%s\" does not exist",
8641 colName, RelationGetRelationName(rel))));
8643 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8644 attnum = attTup->attnum;
8646 if (attnum <= 0)
8647 ereport(ERROR,
8648 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8649 errmsg("cannot alter system column \"%s\"",
8650 colName)));
8652 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8654 if (!missing_ok)
8655 ereport(ERROR,
8656 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8657 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8658 colName, RelationGetRelationName(rel))));
8659 else
8661 ereport(NOTICE,
8662 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8663 colName, RelationGetRelationName(rel))));
8664 heap_freetuple(tuple);
8665 table_close(attrelation, RowExclusiveLock);
8666 return InvalidObjectAddress;
8671 * Mark the column as no longer generated. (The atthasdef flag needs to
8672 * get cleared too, but RemoveAttrDefault will handle that.)
8674 attTup->attgenerated = '\0';
8675 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8677 InvokeObjectPostAlterHook(RelationRelationId,
8678 RelationGetRelid(rel),
8679 attnum);
8680 heap_freetuple(tuple);
8682 table_close(attrelation, RowExclusiveLock);
8685 * Drop the dependency records of the GENERATED expression, in particular
8686 * its INTERNAL dependency on the column, which would otherwise cause
8687 * dependency.c to refuse to perform the deletion.
8689 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8690 if (!OidIsValid(attrdefoid))
8691 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8692 RelationGetRelid(rel), attnum);
8693 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8695 /* Make above changes visible */
8696 CommandCounterIncrement();
8699 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8700 * safety, but at present we do not expect anything to depend on the
8701 * default.
8703 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8704 false, false);
8706 ObjectAddressSubSet(address, RelationRelationId,
8707 RelationGetRelid(rel), attnum);
8708 return address;
8712 * ALTER TABLE ALTER COLUMN SET STATISTICS
8714 * Return value is the address of the modified column
8716 static ObjectAddress
8717 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8719 int newtarget = 0;
8720 bool newtarget_default;
8721 Relation attrelation;
8722 HeapTuple tuple,
8723 newtuple;
8724 Form_pg_attribute attrtuple;
8725 AttrNumber attnum;
8726 ObjectAddress address;
8727 Datum repl_val[Natts_pg_attribute];
8728 bool repl_null[Natts_pg_attribute];
8729 bool repl_repl[Natts_pg_attribute];
8732 * We allow referencing columns by numbers only for indexes, since table
8733 * column numbers could contain gaps if columns are later dropped.
8735 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8736 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8737 !colName)
8738 ereport(ERROR,
8739 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8740 errmsg("cannot refer to non-index column by number")));
8742 /* -1 was used in previous versions for the default setting */
8743 if (newValue && intVal(newValue) != -1)
8745 newtarget = intVal(newValue);
8746 newtarget_default = false;
8748 else
8749 newtarget_default = true;
8751 if (!newtarget_default)
8754 * Limit target to a sane range
8756 if (newtarget < 0)
8758 ereport(ERROR,
8759 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8760 errmsg("statistics target %d is too low",
8761 newtarget)));
8763 else if (newtarget > MAX_STATISTICS_TARGET)
8765 newtarget = MAX_STATISTICS_TARGET;
8766 ereport(WARNING,
8767 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8768 errmsg("lowering statistics target to %d",
8769 newtarget)));
8773 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8775 if (colName)
8777 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8779 if (!HeapTupleIsValid(tuple))
8780 ereport(ERROR,
8781 (errcode(ERRCODE_UNDEFINED_COLUMN),
8782 errmsg("column \"%s\" of relation \"%s\" does not exist",
8783 colName, RelationGetRelationName(rel))));
8785 else
8787 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8789 if (!HeapTupleIsValid(tuple))
8790 ereport(ERROR,
8791 (errcode(ERRCODE_UNDEFINED_COLUMN),
8792 errmsg("column number %d of relation \"%s\" does not exist",
8793 colNum, RelationGetRelationName(rel))));
8796 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8798 attnum = attrtuple->attnum;
8799 if (attnum <= 0)
8800 ereport(ERROR,
8801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8802 errmsg("cannot alter system column \"%s\"",
8803 colName)));
8805 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8806 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8808 if (attnum > rel->rd_index->indnkeyatts)
8809 ereport(ERROR,
8810 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8811 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8812 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8813 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8814 ereport(ERROR,
8815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8816 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8817 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8818 errhint("Alter statistics on table column instead.")));
8821 /* Build new tuple. */
8822 memset(repl_null, false, sizeof(repl_null));
8823 memset(repl_repl, false, sizeof(repl_repl));
8824 if (!newtarget_default)
8825 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8826 else
8827 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8828 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8829 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8830 repl_val, repl_null, repl_repl);
8831 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8833 InvokeObjectPostAlterHook(RelationRelationId,
8834 RelationGetRelid(rel),
8835 attrtuple->attnum);
8836 ObjectAddressSubSet(address, RelationRelationId,
8837 RelationGetRelid(rel), attnum);
8839 heap_freetuple(newtuple);
8841 ReleaseSysCache(tuple);
8843 table_close(attrelation, RowExclusiveLock);
8845 return address;
8849 * Return value is the address of the modified column
8851 static ObjectAddress
8852 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8853 bool isReset, LOCKMODE lockmode)
8855 Relation attrelation;
8856 HeapTuple tuple,
8857 newtuple;
8858 Form_pg_attribute attrtuple;
8859 AttrNumber attnum;
8860 Datum datum,
8861 newOptions;
8862 bool isnull;
8863 ObjectAddress address;
8864 Datum repl_val[Natts_pg_attribute];
8865 bool repl_null[Natts_pg_attribute];
8866 bool repl_repl[Natts_pg_attribute];
8868 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8870 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8872 if (!HeapTupleIsValid(tuple))
8873 ereport(ERROR,
8874 (errcode(ERRCODE_UNDEFINED_COLUMN),
8875 errmsg("column \"%s\" of relation \"%s\" does not exist",
8876 colName, RelationGetRelationName(rel))));
8877 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8879 attnum = attrtuple->attnum;
8880 if (attnum <= 0)
8881 ereport(ERROR,
8882 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8883 errmsg("cannot alter system column \"%s\"",
8884 colName)));
8886 /* Generate new proposed attoptions (text array) */
8887 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8888 &isnull);
8889 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8890 castNode(List, options), NULL, NULL,
8891 false, isReset);
8892 /* Validate new options */
8893 (void) attribute_reloptions(newOptions, true);
8895 /* Build new tuple. */
8896 memset(repl_null, false, sizeof(repl_null));
8897 memset(repl_repl, false, sizeof(repl_repl));
8898 if (newOptions != (Datum) 0)
8899 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8900 else
8901 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8902 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8903 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8904 repl_val, repl_null, repl_repl);
8906 /* Update system catalog. */
8907 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8909 InvokeObjectPostAlterHook(RelationRelationId,
8910 RelationGetRelid(rel),
8911 attrtuple->attnum);
8912 ObjectAddressSubSet(address, RelationRelationId,
8913 RelationGetRelid(rel), attnum);
8915 heap_freetuple(newtuple);
8917 ReleaseSysCache(tuple);
8919 table_close(attrelation, RowExclusiveLock);
8921 return address;
8925 * Helper function for ATExecSetStorage and ATExecSetCompression
8927 * Set the attstorage and/or attcompression fields for index columns
8928 * associated with the specified table column.
8930 static void
8931 SetIndexStorageProperties(Relation rel, Relation attrelation,
8932 AttrNumber attnum,
8933 bool setstorage, char newstorage,
8934 bool setcompression, char newcompression,
8935 LOCKMODE lockmode)
8937 ListCell *lc;
8939 foreach(lc, RelationGetIndexList(rel))
8941 Oid indexoid = lfirst_oid(lc);
8942 Relation indrel;
8943 AttrNumber indattnum = 0;
8944 HeapTuple tuple;
8946 indrel = index_open(indexoid, lockmode);
8948 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8950 if (indrel->rd_index->indkey.values[i] == attnum)
8952 indattnum = i + 1;
8953 break;
8957 if (indattnum == 0)
8959 index_close(indrel, lockmode);
8960 continue;
8963 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8965 if (HeapTupleIsValid(tuple))
8967 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8969 if (setstorage)
8970 attrtuple->attstorage = newstorage;
8972 if (setcompression)
8973 attrtuple->attcompression = newcompression;
8975 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8977 InvokeObjectPostAlterHook(RelationRelationId,
8978 RelationGetRelid(rel),
8979 attrtuple->attnum);
8981 heap_freetuple(tuple);
8984 index_close(indrel, lockmode);
8989 * ALTER TABLE ALTER COLUMN SET STORAGE
8991 * Return value is the address of the modified column
8993 static ObjectAddress
8994 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8996 Relation attrelation;
8997 HeapTuple tuple;
8998 Form_pg_attribute attrtuple;
8999 AttrNumber attnum;
9000 ObjectAddress address;
9002 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9004 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9006 if (!HeapTupleIsValid(tuple))
9007 ereport(ERROR,
9008 (errcode(ERRCODE_UNDEFINED_COLUMN),
9009 errmsg("column \"%s\" of relation \"%s\" does not exist",
9010 colName, RelationGetRelationName(rel))));
9011 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9013 attnum = attrtuple->attnum;
9014 if (attnum <= 0)
9015 ereport(ERROR,
9016 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9017 errmsg("cannot alter system column \"%s\"",
9018 colName)));
9020 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9022 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9024 InvokeObjectPostAlterHook(RelationRelationId,
9025 RelationGetRelid(rel),
9026 attrtuple->attnum);
9029 * Apply the change to indexes as well (only for simple index columns,
9030 * matching behavior of index.c ConstructTupleDescriptor()).
9032 SetIndexStorageProperties(rel, attrelation, attnum,
9033 true, attrtuple->attstorage,
9034 false, 0,
9035 lockmode);
9037 heap_freetuple(tuple);
9039 table_close(attrelation, RowExclusiveLock);
9041 ObjectAddressSubSet(address, RelationRelationId,
9042 RelationGetRelid(rel), attnum);
9043 return address;
9048 * ALTER TABLE DROP COLUMN
9050 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9051 * because we have to decide at runtime whether to recurse or not depending
9052 * on whether attinhcount goes to zero or not. (We can't check this in a
9053 * static pre-pass because it won't handle multiple inheritance situations
9054 * correctly.)
9056 static void
9057 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9058 AlterTableCmd *cmd, LOCKMODE lockmode,
9059 AlterTableUtilityContext *context)
9061 if (rel->rd_rel->reloftype && !recursing)
9062 ereport(ERROR,
9063 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9064 errmsg("cannot drop column from typed table")));
9066 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9067 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9069 if (recurse)
9070 cmd->recurse = true;
9074 * Drops column 'colName' from relation 'rel' and returns the address of the
9075 * dropped column. The column is also dropped (or marked as no longer
9076 * inherited from relation) from the relation's inheritance children, if any.
9078 * In the recursive invocations for inheritance child relations, instead of
9079 * dropping the column directly (if to be dropped at all), its object address
9080 * is added to 'addrs', which must be non-NULL in such invocations. All
9081 * columns are dropped at the same time after all the children have been
9082 * checked recursively.
9084 static ObjectAddress
9085 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9086 DropBehavior behavior,
9087 bool recurse, bool recursing,
9088 bool missing_ok, LOCKMODE lockmode,
9089 ObjectAddresses *addrs)
9091 HeapTuple tuple;
9092 Form_pg_attribute targetatt;
9093 AttrNumber attnum;
9094 List *children;
9095 ObjectAddress object;
9096 bool is_expr;
9098 /* At top level, permission check was done in ATPrepCmd, else do it */
9099 if (recursing)
9100 ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9102 /* Initialize addrs on the first invocation */
9103 Assert(!recursing || addrs != NULL);
9105 /* since this function recurses, it could be driven to stack overflow */
9106 check_stack_depth();
9108 if (!recursing)
9109 addrs = new_object_addresses();
9112 * get the number of the attribute
9114 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9115 if (!HeapTupleIsValid(tuple))
9117 if (!missing_ok)
9119 ereport(ERROR,
9120 (errcode(ERRCODE_UNDEFINED_COLUMN),
9121 errmsg("column \"%s\" of relation \"%s\" does not exist",
9122 colName, RelationGetRelationName(rel))));
9124 else
9126 ereport(NOTICE,
9127 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9128 colName, RelationGetRelationName(rel))));
9129 return InvalidObjectAddress;
9132 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9134 attnum = targetatt->attnum;
9136 /* Can't drop a system attribute */
9137 if (attnum <= 0)
9138 ereport(ERROR,
9139 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9140 errmsg("cannot drop system column \"%s\"",
9141 colName)));
9144 * Don't drop inherited columns, unless recursing (presumably from a drop
9145 * of the parent column)
9147 if (targetatt->attinhcount > 0 && !recursing)
9148 ereport(ERROR,
9149 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9150 errmsg("cannot drop inherited column \"%s\"",
9151 colName)));
9154 * Don't drop columns used in the partition key, either. (If we let this
9155 * go through, the key column's dependencies would cause a cascaded drop
9156 * of the whole table, which is surely not what the user expected.)
9158 if (has_partition_attrs(rel,
9159 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9160 &is_expr))
9161 ereport(ERROR,
9162 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9163 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9164 colName, RelationGetRelationName(rel))));
9166 ReleaseSysCache(tuple);
9169 * Propagate to children as appropriate. Unlike most other ALTER
9170 * routines, we have to do this one level of recursion at a time; we can't
9171 * use find_all_inheritors to do it in one pass.
9173 children =
9174 find_inheritance_children(RelationGetRelid(rel), lockmode);
9176 if (children)
9178 Relation attr_rel;
9179 ListCell *child;
9182 * In case of a partitioned table, the column must be dropped from the
9183 * partitions as well.
9185 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9186 ereport(ERROR,
9187 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9188 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9189 errhint("Do not specify the ONLY keyword.")));
9191 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9192 foreach(child, children)
9194 Oid childrelid = lfirst_oid(child);
9195 Relation childrel;
9196 Form_pg_attribute childatt;
9198 /* find_inheritance_children already got lock */
9199 childrel = table_open(childrelid, NoLock);
9200 CheckTableNotInUse(childrel, "ALTER TABLE");
9202 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9203 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9204 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9205 colName, childrelid);
9206 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9208 if (childatt->attinhcount <= 0) /* shouldn't happen */
9209 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9210 childrelid, colName);
9212 if (recurse)
9215 * If the child column has other definition sources, just
9216 * decrement its inheritance count; if not, recurse to delete
9217 * it.
9219 if (childatt->attinhcount == 1 && !childatt->attislocal)
9221 /* Time to delete this child column, too */
9222 ATExecDropColumn(wqueue, childrel, colName,
9223 behavior, true, true,
9224 false, lockmode, addrs);
9226 else
9228 /* Child column must survive my deletion */
9229 childatt->attinhcount--;
9231 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9233 /* Make update visible */
9234 CommandCounterIncrement();
9237 else
9240 * If we were told to drop ONLY in this table (no recursion),
9241 * we need to mark the inheritors' attributes as locally
9242 * defined rather than inherited.
9244 childatt->attinhcount--;
9245 childatt->attislocal = true;
9247 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9249 /* Make update visible */
9250 CommandCounterIncrement();
9253 heap_freetuple(tuple);
9255 table_close(childrel, NoLock);
9257 table_close(attr_rel, RowExclusiveLock);
9260 /* Add object to delete */
9261 object.classId = RelationRelationId;
9262 object.objectId = RelationGetRelid(rel);
9263 object.objectSubId = attnum;
9264 add_exact_object_address(&object, addrs);
9266 if (!recursing)
9268 /* Recursion has ended, drop everything that was collected */
9269 performMultipleDeletions(addrs, behavior, 0);
9270 free_object_addresses(addrs);
9273 return object;
9277 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9278 * constraint on its children.
9280 static void
9281 ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9282 LOCKMODE lockmode, AlterTableUtilityContext *context)
9284 List *children;
9285 List *newconstrs = NIL;
9286 ListCell *lc;
9287 IndexStmt *indexstmt;
9289 /* No work if not creating a primary key */
9290 if (!IsA(cmd->def, IndexStmt))
9291 return;
9292 indexstmt = castNode(IndexStmt, cmd->def);
9293 if (!indexstmt->primary)
9294 return;
9296 /* No work if no legacy inheritance children are present */
9297 if (rel->rd_rel->relkind != RELKIND_RELATION ||
9298 !rel->rd_rel->relhassubclass)
9299 return;
9301 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
9303 foreach(lc, indexstmt->indexParams)
9305 IndexElem *elem = lfirst_node(IndexElem, lc);
9306 Constraint *nnconstr;
9308 Assert(elem->expr == NULL);
9310 nnconstr = makeNode(Constraint);
9311 nnconstr->contype = CONSTR_NOTNULL;
9312 nnconstr->conname = NULL; /* XXX use PK name? */
9313 nnconstr->inhcount = 1;
9314 nnconstr->deferrable = false;
9315 nnconstr->initdeferred = false;
9316 nnconstr->location = -1;
9317 nnconstr->keys = list_make1(makeString(elem->name));
9318 nnconstr->skip_validation = false;
9319 nnconstr->initially_valid = true;
9321 newconstrs = lappend(newconstrs, nnconstr);
9324 foreach(lc, children)
9326 Oid childrelid = lfirst_oid(lc);
9327 Relation childrel = table_open(childrelid, NoLock);
9328 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
9329 ListCell *lc2;
9331 newcmd->subtype = AT_AddConstraint;
9332 newcmd->recurse = true;
9334 foreach(lc2, newconstrs)
9336 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9337 newcmd->def = lfirst(lc2);
9339 ATPrepCmd(wqueue, childrel, newcmd,
9340 true, false, lockmode, context);
9343 table_close(childrel, NoLock);
9348 * ALTER TABLE ADD INDEX
9350 * There is no such command in the grammar, but parse_utilcmd.c converts
9351 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9352 * us schedule creation of the index at the appropriate time during ALTER.
9354 * Return value is the address of the new index.
9356 static ObjectAddress
9357 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9358 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9360 bool check_rights;
9361 bool skip_build;
9362 bool quiet;
9363 ObjectAddress address;
9365 Assert(IsA(stmt, IndexStmt));
9366 Assert(!stmt->concurrent);
9368 /* The IndexStmt has already been through transformIndexStmt */
9369 Assert(stmt->transformed);
9371 /* suppress schema rights check when rebuilding existing index */
9372 check_rights = !is_rebuild;
9373 /* skip index build if phase 3 will do it or we're reusing an old one */
9374 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9375 /* suppress notices when rebuilding existing index */
9376 quiet = is_rebuild;
9378 address = DefineIndex(RelationGetRelid(rel),
9379 stmt,
9380 InvalidOid, /* no predefined OID */
9381 InvalidOid, /* no parent index */
9382 InvalidOid, /* no parent constraint */
9383 -1, /* total_parts unknown */
9384 true, /* is_alter_table */
9385 check_rights,
9386 false, /* check_not_in_use - we did it already */
9387 skip_build,
9388 quiet);
9391 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9392 * new index instead of building from scratch. Restore associated fields.
9393 * This may store InvalidSubTransactionId in both fields, in which case
9394 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9395 * this after the CCI that made catalog rows visible to any rebuild. The
9396 * DROP of the old edition of this index will have scheduled the storage
9397 * for deletion at commit, so cancel that pending deletion.
9399 if (RelFileNumberIsValid(stmt->oldNumber))
9401 Relation irel = index_open(address.objectId, NoLock);
9403 irel->rd_createSubid = stmt->oldCreateSubid;
9404 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9405 RelationPreserveStorage(irel->rd_locator, true);
9406 index_close(irel, NoLock);
9409 return address;
9413 * ALTER TABLE ADD STATISTICS
9415 * This is no such command in the grammar, but we use this internally to add
9416 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9417 * column type change.
9419 static ObjectAddress
9420 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9421 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9423 ObjectAddress address;
9425 Assert(IsA(stmt, CreateStatsStmt));
9427 /* The CreateStatsStmt has already been through transformStatsStmt */
9428 Assert(stmt->transformed);
9430 address = CreateStatistics(stmt);
9432 return address;
9436 * ALTER TABLE ADD CONSTRAINT USING INDEX
9438 * Returns the address of the new constraint.
9440 static ObjectAddress
9441 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9442 IndexStmt *stmt, LOCKMODE lockmode)
9444 Oid index_oid = stmt->indexOid;
9445 Relation indexRel;
9446 char *indexName;
9447 IndexInfo *indexInfo;
9448 char *constraintName;
9449 char constraintType;
9450 ObjectAddress address;
9451 bits16 flags;
9453 Assert(IsA(stmt, IndexStmt));
9454 Assert(OidIsValid(index_oid));
9455 Assert(stmt->isconstraint);
9458 * Doing this on partitioned tables is not a simple feature to implement,
9459 * so let's punt for now.
9461 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9462 ereport(ERROR,
9463 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9464 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9466 indexRel = index_open(index_oid, AccessShareLock);
9468 indexName = pstrdup(RelationGetRelationName(indexRel));
9470 indexInfo = BuildIndexInfo(indexRel);
9472 /* this should have been checked at parse time */
9473 if (!indexInfo->ii_Unique)
9474 elog(ERROR, "index \"%s\" is not unique", indexName);
9477 * Determine name to assign to constraint. We require a constraint to
9478 * have the same name as the underlying index; therefore, use the index's
9479 * existing name as the default constraint name, and if the user
9480 * explicitly gives some other name for the constraint, rename the index
9481 * to match.
9483 constraintName = stmt->idxname;
9484 if (constraintName == NULL)
9485 constraintName = indexName;
9486 else if (strcmp(constraintName, indexName) != 0)
9488 ereport(NOTICE,
9489 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9490 indexName, constraintName)));
9491 RenameRelationInternal(index_oid, constraintName, false, true);
9494 /* Extra checks needed if making primary key */
9495 if (stmt->primary)
9496 index_check_primary_key(rel, indexInfo, true, stmt);
9498 /* Note we currently don't support EXCLUSION constraints here */
9499 if (stmt->primary)
9500 constraintType = CONSTRAINT_PRIMARY;
9501 else
9502 constraintType = CONSTRAINT_UNIQUE;
9504 /* Create the catalog entries for the constraint */
9505 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9506 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9507 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9508 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9509 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9511 address = index_constraint_create(rel,
9512 index_oid,
9513 InvalidOid,
9514 indexInfo,
9515 constraintName,
9516 constraintType,
9517 flags,
9518 allowSystemTableMods,
9519 false); /* is_internal */
9521 index_close(indexRel, NoLock);
9523 return address;
9527 * ALTER TABLE ADD CONSTRAINT
9529 * Return value is the address of the new constraint; if no constraint was
9530 * added, InvalidObjectAddress is returned.
9532 static ObjectAddress
9533 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9534 Constraint *newConstraint, bool recurse, bool is_readd,
9535 LOCKMODE lockmode)
9537 ObjectAddress address = InvalidObjectAddress;
9539 Assert(IsA(newConstraint, Constraint));
9542 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9543 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9544 * parse_utilcmd.c).
9546 switch (newConstraint->contype)
9548 case CONSTR_CHECK:
9549 case CONSTR_NOTNULL:
9550 address =
9551 ATAddCheckNNConstraint(wqueue, tab, rel,
9552 newConstraint, recurse, false, is_readd,
9553 lockmode);
9554 break;
9556 case CONSTR_FOREIGN:
9559 * Assign or validate constraint name
9561 if (newConstraint->conname)
9563 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9564 RelationGetRelid(rel),
9565 newConstraint->conname))
9566 ereport(ERROR,
9567 (errcode(ERRCODE_DUPLICATE_OBJECT),
9568 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9569 newConstraint->conname,
9570 RelationGetRelationName(rel))));
9572 else
9573 newConstraint->conname =
9574 ChooseConstraintName(RelationGetRelationName(rel),
9575 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9576 "fkey",
9577 RelationGetNamespace(rel),
9578 NIL);
9580 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9581 newConstraint,
9582 recurse, false,
9583 lockmode);
9584 break;
9586 default:
9587 elog(ERROR, "unrecognized constraint type: %d",
9588 (int) newConstraint->contype);
9591 return address;
9595 * Generate the column-name portion of the constraint name for a new foreign
9596 * key given the list of column names that reference the referenced
9597 * table. This will be passed to ChooseConstraintName along with the parent
9598 * table name and the "fkey" suffix.
9600 * We know that less than NAMEDATALEN characters will actually be used, so we
9601 * can truncate the result once we've generated that many.
9603 * XXX see also ChooseExtendedStatisticNameAddition and
9604 * ChooseIndexNameAddition.
9606 static char *
9607 ChooseForeignKeyConstraintNameAddition(List *colnames)
9609 char buf[NAMEDATALEN * 2];
9610 int buflen = 0;
9611 ListCell *lc;
9613 buf[0] = '\0';
9614 foreach(lc, colnames)
9616 const char *name = strVal(lfirst(lc));
9618 if (buflen > 0)
9619 buf[buflen++] = '_'; /* insert _ between names */
9622 * At this point we have buflen <= NAMEDATALEN. name should be less
9623 * than NAMEDATALEN already, but use strlcpy for paranoia.
9625 strlcpy(buf + buflen, name, NAMEDATALEN);
9626 buflen += strlen(buf + buflen);
9627 if (buflen >= NAMEDATALEN)
9628 break;
9630 return pstrdup(buf);
9634 * Add a check or not-null constraint to a single table and its children.
9635 * Returns the address of the constraint added to the parent relation,
9636 * if one gets added, or InvalidObjectAddress otherwise.
9638 * Subroutine for ATExecAddConstraint.
9640 * We must recurse to child tables during execution, rather than using
9641 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9642 * constraints *must* be given the same name, else they won't be seen as
9643 * related later. If the user didn't explicitly specify a name, then
9644 * AddRelationNewConstraints would normally assign different names to the
9645 * child constraints. To fix that, we must capture the name assigned at
9646 * the parent table and pass that down.
9648 static ObjectAddress
9649 ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9650 Constraint *constr, bool recurse, bool recursing,
9651 bool is_readd, LOCKMODE lockmode)
9653 List *newcons;
9654 ListCell *lcon;
9655 List *children;
9656 ListCell *child;
9657 ObjectAddress address = InvalidObjectAddress;
9659 /* Guard against stack overflow due to overly deep inheritance tree. */
9660 check_stack_depth();
9662 /* At top level, permission check was done in ATPrepCmd, else do it */
9663 if (recursing)
9664 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9667 * Call AddRelationNewConstraints to do the work, making sure it works on
9668 * a copy of the Constraint so transformExpr can't modify the original. It
9669 * returns a list of cooked constraints.
9671 * If the constraint ends up getting merged with a pre-existing one, it's
9672 * omitted from the returned list, which is what we want: we do not need
9673 * to do any validation work. That can only happen at child tables,
9674 * though, since we disallow merging at the top level.
9676 newcons = AddRelationNewConstraints(rel, NIL,
9677 list_make1(copyObject(constr)),
9678 recursing || is_readd, /* allow_merge */
9679 !recursing, /* is_local */
9680 is_readd, /* is_internal */
9681 NULL); /* queryString not available
9682 * here */
9684 /* we don't expect more than one constraint here */
9685 Assert(list_length(newcons) <= 1);
9687 /* Add each to-be-validated constraint to Phase 3's queue */
9688 foreach(lcon, newcons)
9690 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9692 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9694 NewConstraint *newcon;
9696 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9697 newcon->name = ccon->name;
9698 newcon->contype = ccon->contype;
9699 newcon->qual = ccon->expr;
9701 tab->constraints = lappend(tab->constraints, newcon);
9704 /* Save the actually assigned name if it was defaulted */
9705 if (constr->conname == NULL)
9706 constr->conname = ccon->name;
9709 * If adding a not-null constraint, set the pg_attribute flag and tell
9710 * phase 3 to verify existing rows, if needed.
9712 if (constr->contype == CONSTR_NOTNULL)
9713 set_attnotnull(wqueue, rel, ccon->attnum,
9714 !ccon->is_no_inherit, lockmode);
9716 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9719 /* At this point we must have a locked-down name to use */
9720 Assert(newcons == NIL || constr->conname != NULL);
9722 /* Advance command counter in case same table is visited multiple times */
9723 CommandCounterIncrement();
9726 * If the constraint got merged with an existing constraint, we're done.
9727 * We mustn't recurse to child tables in this case, because they've
9728 * already got the constraint, and visiting them again would lead to an
9729 * incorrect value for coninhcount.
9731 if (newcons == NIL)
9732 return address;
9735 * If adding a NO INHERIT constraint, no need to find our children.
9737 if (constr->is_no_inherit)
9738 return address;
9741 * Propagate to children as appropriate. Unlike most other ALTER
9742 * routines, we have to do this one level of recursion at a time; we can't
9743 * use find_all_inheritors to do it in one pass.
9745 children =
9746 find_inheritance_children(RelationGetRelid(rel), lockmode);
9749 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9750 * constraint creation only if there are no children currently. Error out
9751 * otherwise.
9753 if (!recurse && children != NIL)
9754 ereport(ERROR,
9755 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9756 errmsg("constraint must be added to child tables too")));
9759 * The constraint must appear as inherited in children, so create a
9760 * modified constraint object to use.
9762 constr = copyObject(constr);
9763 constr->inhcount = 1;
9764 foreach(child, children)
9766 Oid childrelid = lfirst_oid(child);
9767 Relation childrel;
9768 AlteredTableInfo *childtab;
9770 /* find_inheritance_children already got lock */
9771 childrel = table_open(childrelid, NoLock);
9772 CheckTableNotInUse(childrel, "ALTER TABLE");
9774 /* Find or create work queue entry for this table */
9775 childtab = ATGetQueueEntry(wqueue, childrel);
9778 * Recurse to child. XXX if we didn't create a constraint on the
9779 * parent because it already existed, and we do create one on a child,
9780 * should we return that child's constraint ObjectAddress here?
9782 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9783 constr, recurse, true, is_readd, lockmode);
9785 table_close(childrel, NoLock);
9788 return address;
9792 * Add a foreign-key constraint to a single table; return the new constraint's
9793 * address.
9795 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9796 * lock on the rel, and have done appropriate validity checks for it.
9797 * We do permissions checks here, however.
9799 * When the referenced or referencing tables (or both) are partitioned,
9800 * multiple pg_constraint rows are required -- one for each partitioned table
9801 * and each partition on each side (fortunately, not one for every combination
9802 * thereof). We also need action triggers on each leaf partition on the
9803 * referenced side, and check triggers on each leaf partition on the
9804 * referencing side.
9806 static ObjectAddress
9807 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9808 Constraint *fkconstraint,
9809 bool recurse, bool recursing, LOCKMODE lockmode)
9811 Relation pkrel;
9812 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9813 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9814 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9815 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9816 Oid opclasses[INDEX_MAX_KEYS] = {0};
9817 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9818 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9819 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9820 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9821 bool with_period;
9822 bool pk_has_without_overlaps;
9823 int i;
9824 int numfks,
9825 numpks,
9826 numfkdelsetcols;
9827 Oid indexOid;
9828 bool old_check_ok;
9829 ObjectAddress address;
9830 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9833 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9834 * delete rows out from under us.
9836 if (OidIsValid(fkconstraint->old_pktable_oid))
9837 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9838 else
9839 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9842 * Validity checks (permission checks wait till we have the column
9843 * numbers)
9845 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9847 if (!recurse)
9848 ereport(ERROR,
9849 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9850 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9851 RelationGetRelationName(rel),
9852 RelationGetRelationName(pkrel))));
9853 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9854 ereport(ERROR,
9855 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9856 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9857 RelationGetRelationName(rel),
9858 RelationGetRelationName(pkrel)),
9859 errdetail("This feature is not yet supported on partitioned tables.")));
9862 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9863 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9864 ereport(ERROR,
9865 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9866 errmsg("referenced relation \"%s\" is not a table",
9867 RelationGetRelationName(pkrel))));
9869 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9870 ereport(ERROR,
9871 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9872 errmsg("permission denied: \"%s\" is a system catalog",
9873 RelationGetRelationName(pkrel))));
9876 * References from permanent or unlogged tables to temp tables, and from
9877 * permanent tables to unlogged tables, are disallowed because the
9878 * referenced data can vanish out from under us. References from temp
9879 * tables to any other table type are also disallowed, because other
9880 * backends might need to run the RI triggers on the perm table, but they
9881 * can't reliably see tuples in the local buffers of other backends.
9883 switch (rel->rd_rel->relpersistence)
9885 case RELPERSISTENCE_PERMANENT:
9886 if (!RelationIsPermanent(pkrel))
9887 ereport(ERROR,
9888 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9889 errmsg("constraints on permanent tables may reference only permanent tables")));
9890 break;
9891 case RELPERSISTENCE_UNLOGGED:
9892 if (!RelationIsPermanent(pkrel)
9893 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9894 ereport(ERROR,
9895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9896 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9897 break;
9898 case RELPERSISTENCE_TEMP:
9899 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9900 ereport(ERROR,
9901 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9902 errmsg("constraints on temporary tables may reference only temporary tables")));
9903 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9904 ereport(ERROR,
9905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9906 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9907 break;
9911 * Look up the referencing attributes to make sure they exist, and record
9912 * their attnums and type OIDs.
9914 numfks = transformColumnNameList(RelationGetRelid(rel),
9915 fkconstraint->fk_attrs,
9916 fkattnum, fktypoid);
9917 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9918 if (with_period && !fkconstraint->fk_with_period)
9919 ereport(ERROR,
9920 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9921 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9923 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9924 fkconstraint->fk_del_set_cols,
9925 fkdelsetcols, NULL);
9926 validateFkOnDeleteSetColumns(numfks, fkattnum,
9927 numfkdelsetcols, fkdelsetcols,
9928 fkconstraint->fk_del_set_cols);
9931 * If the attribute list for the referenced table was omitted, lookup the
9932 * definition of the primary key and use it. Otherwise, validate the
9933 * supplied attribute list. In either case, discover the index OID and
9934 * index opclasses, and the attnums and type OIDs of the attributes.
9936 if (fkconstraint->pk_attrs == NIL)
9938 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9939 &fkconstraint->pk_attrs,
9940 pkattnum, pktypoid,
9941 opclasses, &pk_has_without_overlaps);
9943 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9944 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9945 ereport(ERROR,
9946 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9947 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9949 else
9951 numpks = transformColumnNameList(RelationGetRelid(pkrel),
9952 fkconstraint->pk_attrs,
9953 pkattnum, pktypoid);
9955 /* Since we got pk_attrs, one should be a period. */
9956 if (with_period && !fkconstraint->pk_with_period)
9957 ereport(ERROR,
9958 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9959 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9961 /* Look for an index matching the column list */
9962 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9963 with_period, opclasses, &pk_has_without_overlaps);
9967 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9968 * must use PERIOD.
9970 if (pk_has_without_overlaps && !with_period)
9971 ereport(ERROR,
9972 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9973 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9976 * Now we can check permissions.
9978 checkFkeyPermissions(pkrel, pkattnum, numpks);
9981 * Check some things for generated columns.
9983 for (i = 0; i < numfks; i++)
9985 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9987 if (attgenerated)
9990 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9992 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9993 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9994 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9995 ereport(ERROR,
9996 (errcode(ERRCODE_SYNTAX_ERROR),
9997 errmsg("invalid %s action for foreign key constraint containing generated column",
9998 "ON UPDATE")));
9999 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10000 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10001 ereport(ERROR,
10002 (errcode(ERRCODE_SYNTAX_ERROR),
10003 errmsg("invalid %s action for foreign key constraint containing generated column",
10004 "ON DELETE")));
10009 * Some actions are currently unsupported for foreign keys using PERIOD.
10011 if (fkconstraint->fk_with_period)
10013 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10014 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10015 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10016 ereport(ERROR,
10017 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10018 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10019 "ON UPDATE"));
10021 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10022 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10023 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10024 ereport(ERROR,
10025 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10026 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10027 "ON DELETE"));
10031 * Look up the equality operators to use in the constraint.
10033 * Note that we have to be careful about the difference between the actual
10034 * PK column type and the opclass' declared input type, which might be
10035 * only binary-compatible with it. The declared opcintype is the right
10036 * thing to probe pg_amop with.
10038 if (numfks != numpks)
10039 ereport(ERROR,
10040 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10041 errmsg("number of referencing and referenced columns for foreign key disagree")));
10044 * On the strength of a previous constraint, we might avoid scanning
10045 * tables to validate this one. See below.
10047 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10048 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10050 for (i = 0; i < numpks; i++)
10052 Oid pktype = pktypoid[i];
10053 Oid fktype = fktypoid[i];
10054 Oid fktyped;
10055 HeapTuple cla_ht;
10056 Form_pg_opclass cla_tup;
10057 Oid amid;
10058 Oid opfamily;
10059 Oid opcintype;
10060 Oid pfeqop;
10061 Oid ppeqop;
10062 Oid ffeqop;
10063 int16 eqstrategy;
10064 Oid pfeqop_right;
10066 /* We need several fields out of the pg_opclass entry */
10067 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10068 if (!HeapTupleIsValid(cla_ht))
10069 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10070 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10071 amid = cla_tup->opcmethod;
10072 opfamily = cla_tup->opcfamily;
10073 opcintype = cla_tup->opcintype;
10074 ReleaseSysCache(cla_ht);
10076 if (with_period)
10078 StrategyNumber rtstrategy;
10079 bool for_overlaps = with_period && i == numpks - 1;
10082 * GiST indexes are required to support temporal foreign keys
10083 * because they combine equals and overlaps.
10085 if (amid != GIST_AM_OID)
10086 elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10088 rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
10091 * An opclass can use whatever strategy numbers it wants, so we
10092 * ask the opclass what number it actually uses instead of our RT*
10093 * constants.
10095 eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
10096 if (eqstrategy == InvalidStrategy)
10098 HeapTuple tuple;
10100 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10101 if (!HeapTupleIsValid(tuple))
10102 elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10104 ereport(ERROR,
10105 errcode(ERRCODE_UNDEFINED_OBJECT),
10106 for_overlaps
10107 ? errmsg("could not identify an overlaps operator for foreign key")
10108 : errmsg("could not identify an equality operator for foreign key"),
10109 errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10110 rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10113 else
10116 * Check it's a btree; currently this can never fail since no
10117 * other index AMs support unique indexes. If we ever did have
10118 * other types of unique indexes, we'd need a way to determine
10119 * which operator strategy number is equality. (We could use
10120 * something like GistTranslateStratnum.)
10122 if (amid != BTREE_AM_OID)
10123 elog(ERROR, "only b-tree indexes are supported for foreign keys");
10124 eqstrategy = BTEqualStrategyNumber;
10128 * There had better be a primary equality operator for the index.
10129 * We'll use it for PK = PK comparisons.
10131 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10132 eqstrategy);
10134 if (!OidIsValid(ppeqop))
10135 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10136 eqstrategy, opcintype, opcintype, opfamily);
10139 * Are there equality operators that take exactly the FK type? Assume
10140 * we should look through any domain here.
10142 fktyped = getBaseType(fktype);
10144 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10145 eqstrategy);
10146 if (OidIsValid(pfeqop))
10148 pfeqop_right = fktyped;
10149 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10150 eqstrategy);
10152 else
10154 /* keep compiler quiet */
10155 pfeqop_right = InvalidOid;
10156 ffeqop = InvalidOid;
10159 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10162 * Otherwise, look for an implicit cast from the FK type to the
10163 * opcintype, and if found, use the primary equality operator.
10164 * This is a bit tricky because opcintype might be a polymorphic
10165 * type such as ANYARRAY or ANYENUM; so what we have to test is
10166 * whether the two actual column types can be concurrently cast to
10167 * that type. (Otherwise, we'd fail to reject combinations such
10168 * as int[] and point[].)
10170 Oid input_typeids[2];
10171 Oid target_typeids[2];
10173 input_typeids[0] = pktype;
10174 input_typeids[1] = fktype;
10175 target_typeids[0] = opcintype;
10176 target_typeids[1] = opcintype;
10177 if (can_coerce_type(2, input_typeids, target_typeids,
10178 COERCION_IMPLICIT))
10180 pfeqop = ffeqop = ppeqop;
10181 pfeqop_right = opcintype;
10185 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10186 ereport(ERROR,
10187 (errcode(ERRCODE_DATATYPE_MISMATCH),
10188 errmsg("foreign key constraint \"%s\" cannot be implemented",
10189 fkconstraint->conname),
10190 errdetail("Key columns \"%s\" and \"%s\" "
10191 "are of incompatible types: %s and %s.",
10192 strVal(list_nth(fkconstraint->fk_attrs, i)),
10193 strVal(list_nth(fkconstraint->pk_attrs, i)),
10194 format_type_be(fktype),
10195 format_type_be(pktype))));
10197 if (old_check_ok)
10200 * When a pfeqop changes, revalidate the constraint. We could
10201 * permit intra-opfamily changes, but that adds subtle complexity
10202 * without any concrete benefit for core types. We need not
10203 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10205 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10206 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10207 old_pfeqop_item);
10209 if (old_check_ok)
10211 Oid old_fktype;
10212 Oid new_fktype;
10213 CoercionPathType old_pathtype;
10214 CoercionPathType new_pathtype;
10215 Oid old_castfunc;
10216 Oid new_castfunc;
10217 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10218 fkattnum[i] - 1);
10221 * Identify coercion pathways from each of the old and new FK-side
10222 * column types to the right (foreign) operand type of the pfeqop.
10223 * We may assume that pg_constraint.conkey is not changing.
10225 old_fktype = attr->atttypid;
10226 new_fktype = fktype;
10227 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10228 &old_castfunc);
10229 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10230 &new_castfunc);
10233 * Upon a change to the cast from the FK column to its pfeqop
10234 * operand, revalidate the constraint. For this evaluation, a
10235 * binary coercion cast is equivalent to no cast at all. While
10236 * type implementors should design implicit casts with an eye
10237 * toward consistency of operations like equality, we cannot
10238 * assume here that they have done so.
10240 * A function with a polymorphic argument could change behavior
10241 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10242 * when the cast destination is polymorphic, we only avoid
10243 * revalidation if the input type has not changed at all. Given
10244 * just the core data types and operator classes, this requirement
10245 * prevents no would-be optimizations.
10247 * If the cast converts from a base type to a domain thereon, then
10248 * that domain type must be the opcintype of the unique index.
10249 * Necessarily, the primary key column must then be of the domain
10250 * type. Since the constraint was previously valid, all values on
10251 * the foreign side necessarily exist on the primary side and in
10252 * turn conform to the domain. Consequently, we need not treat
10253 * domains specially here.
10255 * Since we require that all collations share the same notion of
10256 * equality (which they do, because texteq reduces to bitwise
10257 * equality), we don't compare collation here.
10259 * We need not directly consider the PK type. It's necessarily
10260 * binary coercible to the opcintype of the unique index column,
10261 * and ri_triggers.c will only deal with PK datums in terms of
10262 * that opcintype. Changing the opcintype also changes pfeqop.
10264 old_check_ok = (new_pathtype == old_pathtype &&
10265 new_castfunc == old_castfunc &&
10266 (!IsPolymorphicType(pfeqop_right) ||
10267 new_fktype == old_fktype));
10270 pfeqoperators[i] = pfeqop;
10271 ppeqoperators[i] = ppeqop;
10272 ffeqoperators[i] = ffeqop;
10276 * For FKs with PERIOD we need additional operators to check whether the
10277 * referencing row's range is contained by the aggregated ranges of the
10278 * referenced row(s). For rangetypes and multirangetypes this is
10279 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10280 * support for now. FKs will look these up at "runtime", but we should
10281 * make sure the lookup works here, even if we don't use the values.
10283 if (with_period)
10285 Oid periodoperoid;
10286 Oid aggedperiodoperoid;
10288 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10292 * Create all the constraint and trigger objects, recursing to partitions
10293 * as necessary. First handle the referenced side.
10295 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
10296 indexOid,
10297 InvalidOid, /* no parent constraint */
10298 numfks,
10299 pkattnum,
10300 fkattnum,
10301 pfeqoperators,
10302 ppeqoperators,
10303 ffeqoperators,
10304 numfkdelsetcols,
10305 fkdelsetcols,
10306 old_check_ok,
10307 InvalidOid, InvalidOid,
10308 with_period);
10310 /* Now handle the referencing side. */
10311 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10312 indexOid,
10313 address.objectId,
10314 numfks,
10315 pkattnum,
10316 fkattnum,
10317 pfeqoperators,
10318 ppeqoperators,
10319 ffeqoperators,
10320 numfkdelsetcols,
10321 fkdelsetcols,
10322 old_check_ok,
10323 lockmode,
10324 InvalidOid, InvalidOid,
10325 with_period);
10328 * Done. Close pk table, but keep lock until we've committed.
10330 table_close(pkrel, NoLock);
10332 return address;
10336 * validateFkOnDeleteSetColumns
10337 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10338 * column lists are valid.
10340 void
10341 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10342 int numfksetcols, const int16 *fksetcolsattnums,
10343 List *fksetcols)
10345 for (int i = 0; i < numfksetcols; i++)
10347 int16 setcol_attnum = fksetcolsattnums[i];
10348 bool seen = false;
10350 for (int j = 0; j < numfks; j++)
10352 if (fkattnums[j] == setcol_attnum)
10354 seen = true;
10355 break;
10359 if (!seen)
10361 char *col = strVal(list_nth(fksetcols, i));
10363 ereport(ERROR,
10364 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10365 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10371 * addFkRecurseReferenced
10372 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10373 * side of the constraint
10375 * Create pg_constraint rows for the referenced side of the constraint,
10376 * referencing the parent of the referencing side; also create action triggers
10377 * on leaf partitions. If the table is partitioned, recurse to handle each
10378 * partition.
10380 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10381 * of an ALTER TABLE sequence.
10382 * fkconstraint is the constraint being added.
10383 * rel is the root referencing relation.
10384 * pkrel is the referenced relation; might be a partition, if recursing.
10385 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10386 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10387 * top-level constraint.
10388 * numfks is the number of columns in the foreign key
10389 * pkattnum is the attnum array of referenced attributes.
10390 * fkattnum is the attnum array of referencing attributes.
10391 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10392 * (...) clause
10393 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10394 * NULL/DEFAULT clause
10395 * pf/pp/ffeqoperators are OID array of operators between columns.
10396 * old_check_ok signals that this constraint replaces an existing one that
10397 * was already validated (thus this one doesn't need validation).
10398 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10399 * a partition, are the OIDs of the parent action triggers for DELETE and
10400 * UPDATE respectively.
10402 static ObjectAddress
10403 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10404 Relation pkrel, Oid indexOid, Oid parentConstr,
10405 int numfks,
10406 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10407 Oid *ppeqoperators, Oid *ffeqoperators,
10408 int numfkdelsetcols, int16 *fkdelsetcols,
10409 bool old_check_ok,
10410 Oid parentDelTrigger, Oid parentUpdTrigger,
10411 bool with_period)
10413 ObjectAddress address;
10414 Oid constrOid;
10415 char *conname;
10416 bool conislocal;
10417 int coninhcount;
10418 bool connoinherit;
10419 Oid deleteTriggerOid,
10420 updateTriggerOid;
10423 * Verify relkind for each referenced partition. At the top level, this
10424 * is redundant with a previous check, but we need it when recursing.
10426 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10427 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10428 ereport(ERROR,
10429 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10430 errmsg("referenced relation \"%s\" is not a table",
10431 RelationGetRelationName(pkrel))));
10434 * Caller supplies us with a constraint name; however, it may be used in
10435 * this partition, so come up with a different one in that case.
10437 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10438 RelationGetRelid(rel),
10439 fkconstraint->conname))
10440 conname = ChooseConstraintName(RelationGetRelationName(rel),
10441 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10442 "fkey",
10443 RelationGetNamespace(rel), NIL);
10444 else
10445 conname = fkconstraint->conname;
10447 if (OidIsValid(parentConstr))
10449 conislocal = false;
10450 coninhcount = 1;
10451 connoinherit = false;
10453 else
10455 conislocal = true;
10456 coninhcount = 0;
10459 * always inherit for partitioned tables, never for legacy inheritance
10461 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10465 * Record the FK constraint in pg_constraint.
10467 constrOid = CreateConstraintEntry(conname,
10468 RelationGetNamespace(rel),
10469 CONSTRAINT_FOREIGN,
10470 fkconstraint->deferrable,
10471 fkconstraint->initdeferred,
10472 fkconstraint->initially_valid,
10473 parentConstr,
10474 RelationGetRelid(rel),
10475 fkattnum,
10476 numfks,
10477 numfks,
10478 InvalidOid, /* not a domain constraint */
10479 indexOid,
10480 RelationGetRelid(pkrel),
10481 pkattnum,
10482 pfeqoperators,
10483 ppeqoperators,
10484 ffeqoperators,
10485 numfks,
10486 fkconstraint->fk_upd_action,
10487 fkconstraint->fk_del_action,
10488 fkdelsetcols,
10489 numfkdelsetcols,
10490 fkconstraint->fk_matchtype,
10491 NULL, /* no exclusion constraint */
10492 NULL, /* no check constraint */
10493 NULL,
10494 conislocal, /* islocal */
10495 coninhcount, /* inhcount */
10496 connoinherit, /* conNoInherit */
10497 with_period, /* conPeriod */
10498 false); /* is_internal */
10500 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10503 * Mark the child constraint as part of the parent constraint; it must not
10504 * be dropped on its own. (This constraint is deleted when the partition
10505 * is detached, but a special check needs to occur that the partition
10506 * contains no referenced values.)
10508 if (OidIsValid(parentConstr))
10510 ObjectAddress referenced;
10512 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10513 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10516 /* make new constraint visible, in case we add more */
10517 CommandCounterIncrement();
10520 * Create the action triggers that enforce the constraint.
10522 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10523 fkconstraint,
10524 constrOid, indexOid,
10525 parentDelTrigger, parentUpdTrigger,
10526 &deleteTriggerOid, &updateTriggerOid);
10529 * If the referenced table is partitioned, recurse on ourselves to handle
10530 * each partition. We need one pg_constraint row created for each
10531 * partition in addition to the pg_constraint row for the parent table.
10533 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10535 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10537 for (int i = 0; i < pd->nparts; i++)
10539 Relation partRel;
10540 AttrMap *map;
10541 AttrNumber *mapped_pkattnum;
10542 Oid partIndexId;
10544 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10547 * Map the attribute numbers in the referenced side of the FK
10548 * definition to match the partition's column layout.
10550 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10551 RelationGetDescr(pkrel),
10552 false);
10553 if (map)
10555 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10556 for (int j = 0; j < numfks; j++)
10557 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10559 else
10560 mapped_pkattnum = pkattnum;
10562 /* do the deed */
10563 partIndexId = index_get_partition(partRel, indexOid);
10564 if (!OidIsValid(partIndexId))
10565 elog(ERROR, "index for %u not found in partition %s",
10566 indexOid, RelationGetRelationName(partRel));
10567 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10568 partIndexId, constrOid, numfks,
10569 mapped_pkattnum, fkattnum,
10570 pfeqoperators, ppeqoperators, ffeqoperators,
10571 numfkdelsetcols, fkdelsetcols,
10572 old_check_ok,
10573 deleteTriggerOid, updateTriggerOid,
10574 with_period);
10576 /* Done -- clean up (but keep the lock) */
10577 table_close(partRel, NoLock);
10578 if (map)
10580 pfree(mapped_pkattnum);
10581 free_attrmap(map);
10586 return address;
10590 * addFkRecurseReferencing
10591 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10593 * If the referencing relation is a plain relation, create the necessary check
10594 * triggers that implement the constraint, and set up for Phase 3 constraint
10595 * verification. If the referencing relation is a partitioned table, then
10596 * we create a pg_constraint row for it and recurse on this routine for each
10597 * partition.
10599 * We assume that the referenced relation is locked against concurrent
10600 * deletions. If it's a partitioned relation, every partition must be so
10601 * locked.
10603 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10604 * of an ALTER TABLE sequence.
10605 * fkconstraint is the constraint being added.
10606 * rel is the referencing relation; might be a partition, if recursing.
10607 * pkrel is the root referenced relation.
10608 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10609 * parentConstr is the OID of the parent constraint (there is always one).
10610 * numfks is the number of columns in the foreign key
10611 * pkattnum is the attnum array of referenced attributes.
10612 * fkattnum is the attnum array of referencing attributes.
10613 * pf/pp/ffeqoperators are OID array of operators between columns.
10614 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10615 * (...) clause
10616 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10617 * NULL/DEFAULT clause
10618 * old_check_ok signals that this constraint replaces an existing one that
10619 * was already validated (thus this one doesn't need validation).
10620 * lockmode is the lockmode to acquire on partitions when recursing.
10621 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10622 * a partition, are the OIDs of the parent check triggers for INSERT and
10623 * UPDATE respectively.
10625 static void
10626 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10627 Relation pkrel, Oid indexOid, Oid parentConstr,
10628 int numfks, int16 *pkattnum, int16 *fkattnum,
10629 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10630 int numfkdelsetcols, int16 *fkdelsetcols,
10631 bool old_check_ok, LOCKMODE lockmode,
10632 Oid parentInsTrigger, Oid parentUpdTrigger,
10633 bool with_period)
10635 Oid insertTriggerOid,
10636 updateTriggerOid;
10638 Assert(OidIsValid(parentConstr));
10640 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10641 ereport(ERROR,
10642 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10643 errmsg("foreign key constraints are not supported on foreign tables")));
10646 * Add the check triggers to it and, if necessary, schedule it to be
10647 * checked in Phase 3.
10649 * If the relation is partitioned, drill down to do it to its partitions.
10651 createForeignKeyCheckTriggers(RelationGetRelid(rel),
10652 RelationGetRelid(pkrel),
10653 fkconstraint,
10654 parentConstr,
10655 indexOid,
10656 parentInsTrigger, parentUpdTrigger,
10657 &insertTriggerOid, &updateTriggerOid);
10659 if (rel->rd_rel->relkind == RELKIND_RELATION)
10662 * Tell Phase 3 to check that the constraint is satisfied by existing
10663 * rows. We can skip this during table creation, when requested
10664 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10665 * and when we're recreating a constraint following a SET DATA TYPE
10666 * operation that did not impugn its validity.
10668 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10670 NewConstraint *newcon;
10671 AlteredTableInfo *tab;
10673 tab = ATGetQueueEntry(wqueue, rel);
10675 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10676 newcon->name = get_constraint_name(parentConstr);
10677 newcon->contype = CONSTR_FOREIGN;
10678 newcon->refrelid = RelationGetRelid(pkrel);
10679 newcon->refindid = indexOid;
10680 newcon->conid = parentConstr;
10681 newcon->conwithperiod = fkconstraint->fk_with_period;
10682 newcon->qual = (Node *) fkconstraint;
10684 tab->constraints = lappend(tab->constraints, newcon);
10687 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10689 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10690 Relation trigrel;
10693 * Triggers of the foreign keys will be manipulated a bunch of times
10694 * in the loop below. To avoid repeatedly opening/closing the trigger
10695 * catalog relation, we open it here and pass it to the subroutines
10696 * called below.
10698 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10701 * Recurse to take appropriate action on each partition; either we
10702 * find an existing constraint to reparent to ours, or we create a new
10703 * one.
10705 for (int i = 0; i < pd->nparts; i++)
10707 Oid partitionId = pd->oids[i];
10708 Relation partition = table_open(partitionId, lockmode);
10709 List *partFKs;
10710 AttrMap *attmap;
10711 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10712 bool attached;
10713 char *conname;
10714 Oid constrOid;
10715 ObjectAddress address,
10716 referenced;
10717 ListCell *cell;
10719 CheckTableNotInUse(partition, "ALTER TABLE");
10721 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10722 RelationGetDescr(rel),
10723 false);
10724 for (int j = 0; j < numfks; j++)
10725 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10727 /* Check whether an existing constraint can be repurposed */
10728 partFKs = copyObject(RelationGetFKeyList(partition));
10729 attached = false;
10730 foreach(cell, partFKs)
10732 ForeignKeyCacheInfo *fk;
10734 fk = lfirst_node(ForeignKeyCacheInfo, cell);
10735 if (tryAttachPartitionForeignKey(fk,
10736 partitionId,
10737 parentConstr,
10738 numfks,
10739 mapped_fkattnum,
10740 pkattnum,
10741 pfeqoperators,
10742 insertTriggerOid,
10743 updateTriggerOid,
10744 trigrel))
10746 attached = true;
10747 break;
10750 if (attached)
10752 table_close(partition, NoLock);
10753 continue;
10757 * No luck finding a good constraint to reuse; create our own.
10759 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10760 RelationGetRelid(partition),
10761 fkconstraint->conname))
10762 conname = ChooseConstraintName(RelationGetRelationName(partition),
10763 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10764 "fkey",
10765 RelationGetNamespace(partition), NIL);
10766 else
10767 conname = fkconstraint->conname;
10768 constrOid =
10769 CreateConstraintEntry(conname,
10770 RelationGetNamespace(partition),
10771 CONSTRAINT_FOREIGN,
10772 fkconstraint->deferrable,
10773 fkconstraint->initdeferred,
10774 fkconstraint->initially_valid,
10775 parentConstr,
10776 partitionId,
10777 mapped_fkattnum,
10778 numfks,
10779 numfks,
10780 InvalidOid,
10781 indexOid,
10782 RelationGetRelid(pkrel),
10783 pkattnum,
10784 pfeqoperators,
10785 ppeqoperators,
10786 ffeqoperators,
10787 numfks,
10788 fkconstraint->fk_upd_action,
10789 fkconstraint->fk_del_action,
10790 fkdelsetcols,
10791 numfkdelsetcols,
10792 fkconstraint->fk_matchtype,
10793 NULL,
10794 NULL,
10795 NULL,
10796 false,
10798 false,
10799 with_period, /* conPeriod */
10800 false);
10803 * Give this constraint partition-type dependencies on the parent
10804 * constraint as well as the table.
10806 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10807 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10808 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10809 ObjectAddressSet(referenced, RelationRelationId, partitionId);
10810 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10812 /* Make all this visible before recursing */
10813 CommandCounterIncrement();
10815 /* call ourselves to finalize the creation and we're done */
10816 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10817 indexOid,
10818 constrOid,
10819 numfks,
10820 pkattnum,
10821 mapped_fkattnum,
10822 pfeqoperators,
10823 ppeqoperators,
10824 ffeqoperators,
10825 numfkdelsetcols,
10826 fkdelsetcols,
10827 old_check_ok,
10828 lockmode,
10829 insertTriggerOid,
10830 updateTriggerOid,
10831 with_period);
10833 table_close(partition, NoLock);
10836 table_close(trigrel, RowExclusiveLock);
10841 * CloneForeignKeyConstraints
10842 * Clone foreign keys from a partitioned table to a newly acquired
10843 * partition.
10845 * partitionRel is a partition of parentRel, so we can be certain that it has
10846 * the same columns with the same datatypes. The columns may be in different
10847 * order, though.
10849 * wqueue must be passed to set up phase 3 constraint checking, unless the
10850 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10851 * PARTITION OF).
10853 static void
10854 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10855 Relation partitionRel)
10857 /* This only works for declarative partitioning */
10858 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10861 * Clone constraints for which the parent is on the referenced side.
10863 CloneFkReferenced(parentRel, partitionRel);
10866 * Now clone constraints where the parent is on the referencing side.
10868 CloneFkReferencing(wqueue, parentRel, partitionRel);
10872 * CloneFkReferenced
10873 * Subroutine for CloneForeignKeyConstraints
10875 * Find all the FKs that have the parent relation on the referenced side;
10876 * clone those constraints to the given partition. This is to be called
10877 * when the partition is being created or attached.
10879 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10881 * This recurses to partitions, if the relation being attached is partitioned.
10882 * Recursion is done by calling addFkRecurseReferenced.
10884 static void
10885 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10887 Relation pg_constraint;
10888 AttrMap *attmap;
10889 ListCell *cell;
10890 SysScanDesc scan;
10891 ScanKeyData key[2];
10892 HeapTuple tuple;
10893 List *clone = NIL;
10894 Relation trigrel;
10897 * Search for any constraints where this partition's parent is in the
10898 * referenced side. However, we must not clone any constraint whose
10899 * parent constraint is also going to be cloned, to avoid duplicates. So
10900 * do it in two steps: first construct the list of constraints to clone,
10901 * then go over that list cloning those whose parents are not in the list.
10902 * (We must not rely on the parent being seen first, since the catalog
10903 * scan could return children first.)
10905 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10906 ScanKeyInit(&key[0],
10907 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10908 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10909 ScanKeyInit(&key[1],
10910 Anum_pg_constraint_contype, BTEqualStrategyNumber,
10911 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10912 /* This is a seqscan, as we don't have a usable index ... */
10913 scan = systable_beginscan(pg_constraint, InvalidOid, true,
10914 NULL, 2, key);
10915 while ((tuple = systable_getnext(scan)) != NULL)
10917 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10919 clone = lappend_oid(clone, constrForm->oid);
10921 systable_endscan(scan);
10922 table_close(pg_constraint, RowShareLock);
10925 * Triggers of the foreign keys will be manipulated a bunch of times in
10926 * the loop below. To avoid repeatedly opening/closing the trigger
10927 * catalog relation, we open it here and pass it to the subroutines called
10928 * below.
10930 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10932 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10933 RelationGetDescr(parentRel),
10934 false);
10935 foreach(cell, clone)
10937 Oid constrOid = lfirst_oid(cell);
10938 Form_pg_constraint constrForm;
10939 Relation fkRel;
10940 Oid indexOid;
10941 Oid partIndexId;
10942 int numfks;
10943 AttrNumber conkey[INDEX_MAX_KEYS];
10944 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10945 AttrNumber confkey[INDEX_MAX_KEYS];
10946 Oid conpfeqop[INDEX_MAX_KEYS];
10947 Oid conppeqop[INDEX_MAX_KEYS];
10948 Oid conffeqop[INDEX_MAX_KEYS];
10949 int numfkdelsetcols;
10950 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10951 Constraint *fkconstraint;
10952 Oid deleteTriggerOid,
10953 updateTriggerOid;
10955 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10956 if (!HeapTupleIsValid(tuple))
10957 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10958 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10961 * As explained above: don't try to clone a constraint for which we're
10962 * going to clone the parent.
10964 if (list_member_oid(clone, constrForm->conparentid))
10966 ReleaseSysCache(tuple);
10967 continue;
10971 * Don't clone self-referencing foreign keys, which can be in the
10972 * partitioned table or in the partition-to-be.
10974 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10975 constrForm->conrelid == RelationGetRelid(partitionRel))
10977 ReleaseSysCache(tuple);
10978 continue;
10982 * Because we're only expanding the key space at the referenced side,
10983 * we don't need to prevent any operation in the referencing table, so
10984 * AccessShareLock suffices (assumes that dropping the constraint
10985 * acquires AEL).
10987 fkRel = table_open(constrForm->conrelid, AccessShareLock);
10989 indexOid = constrForm->conindid;
10990 DeconstructFkConstraintRow(tuple,
10991 &numfks,
10992 conkey,
10993 confkey,
10994 conpfeqop,
10995 conppeqop,
10996 conffeqop,
10997 &numfkdelsetcols,
10998 confdelsetcols);
11000 for (int i = 0; i < numfks; i++)
11001 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11003 fkconstraint = makeNode(Constraint);
11004 fkconstraint->contype = CONSTRAINT_FOREIGN;
11005 fkconstraint->conname = NameStr(constrForm->conname);
11006 fkconstraint->deferrable = constrForm->condeferrable;
11007 fkconstraint->initdeferred = constrForm->condeferred;
11008 fkconstraint->location = -1;
11009 fkconstraint->pktable = NULL;
11010 /* ->fk_attrs determined below */
11011 fkconstraint->pk_attrs = NIL;
11012 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11013 fkconstraint->fk_upd_action = constrForm->confupdtype;
11014 fkconstraint->fk_del_action = constrForm->confdeltype;
11015 fkconstraint->fk_del_set_cols = NIL;
11016 fkconstraint->old_conpfeqop = NIL;
11017 fkconstraint->old_pktable_oid = InvalidOid;
11018 fkconstraint->skip_validation = false;
11019 fkconstraint->initially_valid = true;
11021 /* set up colnames that are used to generate the constraint name */
11022 for (int i = 0; i < numfks; i++)
11024 Form_pg_attribute att;
11026 att = TupleDescAttr(RelationGetDescr(fkRel),
11027 conkey[i] - 1);
11028 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11029 makeString(NameStr(att->attname)));
11033 * Add the new foreign key constraint pointing to the new partition.
11034 * Because this new partition appears in the referenced side of the
11035 * constraint, we don't need to set up for Phase 3 check.
11037 partIndexId = index_get_partition(partitionRel, indexOid);
11038 if (!OidIsValid(partIndexId))
11039 elog(ERROR, "index for %u not found in partition %s",
11040 indexOid, RelationGetRelationName(partitionRel));
11043 * Get the "action" triggers belonging to the constraint to pass as
11044 * parent OIDs for similar triggers that will be created on the
11045 * partition in addFkRecurseReferenced().
11047 GetForeignKeyActionTriggers(trigrel, constrOid,
11048 constrForm->confrelid, constrForm->conrelid,
11049 &deleteTriggerOid, &updateTriggerOid);
11051 addFkRecurseReferenced(NULL,
11052 fkconstraint,
11053 fkRel,
11054 partitionRel,
11055 partIndexId,
11056 constrOid,
11057 numfks,
11058 mapped_confkey,
11059 conkey,
11060 conpfeqop,
11061 conppeqop,
11062 conffeqop,
11063 numfkdelsetcols,
11064 confdelsetcols,
11065 true,
11066 deleteTriggerOid,
11067 updateTriggerOid,
11068 constrForm->conperiod);
11070 table_close(fkRel, NoLock);
11071 ReleaseSysCache(tuple);
11074 table_close(trigrel, RowExclusiveLock);
11078 * CloneFkReferencing
11079 * Subroutine for CloneForeignKeyConstraints
11081 * For each FK constraint of the parent relation in the given list, find an
11082 * equivalent constraint in its partition relation that can be reparented;
11083 * if one cannot be found, create a new constraint in the partition as its
11084 * child.
11086 * If wqueue is given, it is used to set up phase-3 verification for each
11087 * cloned constraint; if omitted, we assume that such verification is not
11088 * needed (example: the partition is being created anew).
11090 static void
11091 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11093 AttrMap *attmap;
11094 List *partFKs;
11095 List *clone = NIL;
11096 ListCell *cell;
11097 Relation trigrel;
11099 /* obtain a list of constraints that we need to clone */
11100 foreach(cell, RelationGetFKeyList(parentRel))
11102 ForeignKeyCacheInfo *fk = lfirst(cell);
11104 clone = lappend_oid(clone, fk->conoid);
11108 * Silently do nothing if there's nothing to do. In particular, this
11109 * avoids throwing a spurious error for foreign tables.
11111 if (clone == NIL)
11112 return;
11114 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11115 ereport(ERROR,
11116 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11117 errmsg("foreign key constraints are not supported on foreign tables")));
11120 * Triggers of the foreign keys will be manipulated a bunch of times in
11121 * the loop below. To avoid repeatedly opening/closing the trigger
11122 * catalog relation, we open it here and pass it to the subroutines called
11123 * below.
11125 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11128 * The constraint key may differ, if the columns in the partition are
11129 * different. This map is used to convert them.
11131 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11132 RelationGetDescr(parentRel),
11133 false);
11135 partFKs = copyObject(RelationGetFKeyList(partRel));
11137 foreach(cell, clone)
11139 Oid parentConstrOid = lfirst_oid(cell);
11140 Form_pg_constraint constrForm;
11141 Relation pkrel;
11142 HeapTuple tuple;
11143 int numfks;
11144 AttrNumber conkey[INDEX_MAX_KEYS];
11145 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11146 AttrNumber confkey[INDEX_MAX_KEYS];
11147 Oid conpfeqop[INDEX_MAX_KEYS];
11148 Oid conppeqop[INDEX_MAX_KEYS];
11149 Oid conffeqop[INDEX_MAX_KEYS];
11150 int numfkdelsetcols;
11151 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11152 Constraint *fkconstraint;
11153 bool attached;
11154 Oid indexOid;
11155 Oid constrOid;
11156 ObjectAddress address,
11157 referenced;
11158 ListCell *lc;
11159 Oid insertTriggerOid,
11160 updateTriggerOid;
11161 bool with_period;
11163 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11164 if (!HeapTupleIsValid(tuple))
11165 elog(ERROR, "cache lookup failed for constraint %u",
11166 parentConstrOid);
11167 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11169 /* Don't clone constraints whose parents are being cloned */
11170 if (list_member_oid(clone, constrForm->conparentid))
11172 ReleaseSysCache(tuple);
11173 continue;
11177 * Need to prevent concurrent deletions. If pkrel is a partitioned
11178 * relation, that means to lock all partitions.
11180 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11181 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11182 (void) find_all_inheritors(RelationGetRelid(pkrel),
11183 ShareRowExclusiveLock, NULL);
11185 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11186 conpfeqop, conppeqop, conffeqop,
11187 &numfkdelsetcols, confdelsetcols);
11188 for (int i = 0; i < numfks; i++)
11189 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11192 * Get the "check" triggers belonging to the constraint to pass as
11193 * parent OIDs for similar triggers that will be created on the
11194 * partition in addFkRecurseReferencing(). They are also passed to
11195 * tryAttachPartitionForeignKey() below to simply assign as parents to
11196 * the partition's existing "check" triggers, that is, if the
11197 * corresponding constraints is deemed attachable to the parent
11198 * constraint.
11200 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11201 constrForm->confrelid, constrForm->conrelid,
11202 &insertTriggerOid, &updateTriggerOid);
11205 * Before creating a new constraint, see whether any existing FKs are
11206 * fit for the purpose. If one is, attach the parent constraint to
11207 * it, and don't clone anything. This way we avoid the expensive
11208 * verification step and don't end up with a duplicate FK, and we
11209 * don't need to recurse to partitions for this constraint.
11211 attached = false;
11212 foreach(lc, partFKs)
11214 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11216 if (tryAttachPartitionForeignKey(fk,
11217 RelationGetRelid(partRel),
11218 parentConstrOid,
11219 numfks,
11220 mapped_conkey,
11221 confkey,
11222 conpfeqop,
11223 insertTriggerOid,
11224 updateTriggerOid,
11225 trigrel))
11227 attached = true;
11228 table_close(pkrel, NoLock);
11229 break;
11232 if (attached)
11234 ReleaseSysCache(tuple);
11235 continue;
11238 /* No dice. Set up to create our own constraint */
11239 fkconstraint = makeNode(Constraint);
11240 fkconstraint->contype = CONSTRAINT_FOREIGN;
11241 /* ->conname determined below */
11242 fkconstraint->deferrable = constrForm->condeferrable;
11243 fkconstraint->initdeferred = constrForm->condeferred;
11244 fkconstraint->location = -1;
11245 fkconstraint->pktable = NULL;
11246 /* ->fk_attrs determined below */
11247 fkconstraint->pk_attrs = NIL;
11248 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11249 fkconstraint->fk_upd_action = constrForm->confupdtype;
11250 fkconstraint->fk_del_action = constrForm->confdeltype;
11251 fkconstraint->fk_del_set_cols = NIL;
11252 fkconstraint->old_conpfeqop = NIL;
11253 fkconstraint->old_pktable_oid = InvalidOid;
11254 fkconstraint->skip_validation = false;
11255 fkconstraint->initially_valid = true;
11256 for (int i = 0; i < numfks; i++)
11258 Form_pg_attribute att;
11260 att = TupleDescAttr(RelationGetDescr(partRel),
11261 mapped_conkey[i] - 1);
11262 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11263 makeString(NameStr(att->attname)));
11265 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
11266 RelationGetRelid(partRel),
11267 NameStr(constrForm->conname)))
11268 fkconstraint->conname =
11269 ChooseConstraintName(RelationGetRelationName(partRel),
11270 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
11271 "fkey",
11272 RelationGetNamespace(partRel), NIL);
11273 else
11274 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
11276 indexOid = constrForm->conindid;
11277 with_period = constrForm->conperiod;
11278 constrOid =
11279 CreateConstraintEntry(fkconstraint->conname,
11280 constrForm->connamespace,
11281 CONSTRAINT_FOREIGN,
11282 fkconstraint->deferrable,
11283 fkconstraint->initdeferred,
11284 constrForm->convalidated,
11285 parentConstrOid,
11286 RelationGetRelid(partRel),
11287 mapped_conkey,
11288 numfks,
11289 numfks,
11290 InvalidOid, /* not a domain constraint */
11291 indexOid,
11292 constrForm->confrelid, /* same foreign rel */
11293 confkey,
11294 conpfeqop,
11295 conppeqop,
11296 conffeqop,
11297 numfks,
11298 fkconstraint->fk_upd_action,
11299 fkconstraint->fk_del_action,
11300 confdelsetcols,
11301 numfkdelsetcols,
11302 fkconstraint->fk_matchtype,
11303 NULL,
11304 NULL,
11305 NULL,
11306 false, /* islocal */
11307 1, /* inhcount */
11308 false, /* conNoInherit */
11309 with_period, /* conPeriod */
11310 true);
11312 /* Set up partition dependencies for the new constraint */
11313 ObjectAddressSet(address, ConstraintRelationId, constrOid);
11314 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
11315 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
11316 ObjectAddressSet(referenced, RelationRelationId,
11317 RelationGetRelid(partRel));
11318 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
11320 /* Done with the cloned constraint's tuple */
11321 ReleaseSysCache(tuple);
11323 /* Make all this visible before recursing */
11324 CommandCounterIncrement();
11326 addFkRecurseReferencing(wqueue,
11327 fkconstraint,
11328 partRel,
11329 pkrel,
11330 indexOid,
11331 constrOid,
11332 numfks,
11333 confkey,
11334 mapped_conkey,
11335 conpfeqop,
11336 conppeqop,
11337 conffeqop,
11338 numfkdelsetcols,
11339 confdelsetcols,
11340 false, /* no old check exists */
11341 AccessExclusiveLock,
11342 insertTriggerOid,
11343 updateTriggerOid,
11344 with_period);
11345 table_close(pkrel, NoLock);
11348 table_close(trigrel, RowExclusiveLock);
11352 * When the parent of a partition receives [the referencing side of] a foreign
11353 * key, we must propagate that foreign key to the partition. However, the
11354 * partition might already have an equivalent foreign key; this routine
11355 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11356 * by the other parameters. If they are equivalent, create the link between
11357 * the two constraints and return true.
11359 * If the given FK does not match the one defined by rest of the params,
11360 * return false.
11362 static bool
11363 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11364 Oid partRelid,
11365 Oid parentConstrOid,
11366 int numfks,
11367 AttrNumber *mapped_conkey,
11368 AttrNumber *confkey,
11369 Oid *conpfeqop,
11370 Oid parentInsTrigger,
11371 Oid parentUpdTrigger,
11372 Relation trigrel)
11374 HeapTuple parentConstrTup;
11375 Form_pg_constraint parentConstr;
11376 HeapTuple partcontup;
11377 Form_pg_constraint partConstr;
11378 ScanKeyData key;
11379 SysScanDesc scan;
11380 HeapTuple trigtup;
11381 Oid insertTriggerOid,
11382 updateTriggerOid;
11384 parentConstrTup = SearchSysCache1(CONSTROID,
11385 ObjectIdGetDatum(parentConstrOid));
11386 if (!HeapTupleIsValid(parentConstrTup))
11387 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11388 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11391 * Do some quick & easy initial checks. If any of these fail, we cannot
11392 * use this constraint.
11394 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11396 ReleaseSysCache(parentConstrTup);
11397 return false;
11399 for (int i = 0; i < numfks; i++)
11401 if (fk->conkey[i] != mapped_conkey[i] ||
11402 fk->confkey[i] != confkey[i] ||
11403 fk->conpfeqop[i] != conpfeqop[i])
11405 ReleaseSysCache(parentConstrTup);
11406 return false;
11411 * Looks good so far; do some more extensive checks. Presumably the check
11412 * for 'convalidated' could be dropped, since we don't really care about
11413 * that, but let's be careful for now.
11415 partcontup = SearchSysCache1(CONSTROID,
11416 ObjectIdGetDatum(fk->conoid));
11417 if (!HeapTupleIsValid(partcontup))
11418 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11419 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11420 if (OidIsValid(partConstr->conparentid) ||
11421 !partConstr->convalidated ||
11422 partConstr->condeferrable != parentConstr->condeferrable ||
11423 partConstr->condeferred != parentConstr->condeferred ||
11424 partConstr->confupdtype != parentConstr->confupdtype ||
11425 partConstr->confdeltype != parentConstr->confdeltype ||
11426 partConstr->confmatchtype != parentConstr->confmatchtype)
11428 ReleaseSysCache(parentConstrTup);
11429 ReleaseSysCache(partcontup);
11430 return false;
11433 ReleaseSysCache(partcontup);
11434 ReleaseSysCache(parentConstrTup);
11437 * Looks good! Attach this constraint. The action triggers in the new
11438 * partition become redundant -- the parent table already has equivalent
11439 * ones, and those will be able to reach the partition. Remove the ones
11440 * in the partition. We identify them because they have our constraint
11441 * OID, as well as being on the referenced rel.
11443 ScanKeyInit(&key,
11444 Anum_pg_trigger_tgconstraint,
11445 BTEqualStrategyNumber, F_OIDEQ,
11446 ObjectIdGetDatum(fk->conoid));
11447 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11448 NULL, 1, &key);
11449 while ((trigtup = systable_getnext(scan)) != NULL)
11451 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11452 ObjectAddress trigger;
11454 if (trgform->tgconstrrelid != fk->conrelid)
11455 continue;
11456 if (trgform->tgrelid != fk->confrelid)
11457 continue;
11460 * The constraint is originally set up to contain this trigger as an
11461 * implementation object, so there's a dependency record that links
11462 * the two; however, since the trigger is no longer needed, we remove
11463 * the dependency link in order to be able to drop the trigger while
11464 * keeping the constraint intact.
11466 deleteDependencyRecordsFor(TriggerRelationId,
11467 trgform->oid,
11468 false);
11469 /* make dependency deletion visible to performDeletion */
11470 CommandCounterIncrement();
11471 ObjectAddressSet(trigger, TriggerRelationId,
11472 trgform->oid);
11473 performDeletion(&trigger, DROP_RESTRICT, 0);
11474 /* make trigger drop visible, in case the loop iterates */
11475 CommandCounterIncrement();
11478 systable_endscan(scan);
11480 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11483 * Like the constraint, attach partition's "check" triggers to the
11484 * corresponding parent triggers.
11486 GetForeignKeyCheckTriggers(trigrel,
11487 fk->conoid, fk->confrelid, fk->conrelid,
11488 &insertTriggerOid, &updateTriggerOid);
11489 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11490 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11491 partRelid);
11492 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11493 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11494 partRelid);
11496 CommandCounterIncrement();
11497 return true;
11501 * GetForeignKeyActionTriggers
11502 * Returns delete and update "action" triggers of the given relation
11503 * belonging to the given constraint
11505 static void
11506 GetForeignKeyActionTriggers(Relation trigrel,
11507 Oid conoid, Oid confrelid, Oid conrelid,
11508 Oid *deleteTriggerOid,
11509 Oid *updateTriggerOid)
11511 ScanKeyData key;
11512 SysScanDesc scan;
11513 HeapTuple trigtup;
11515 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11516 ScanKeyInit(&key,
11517 Anum_pg_trigger_tgconstraint,
11518 BTEqualStrategyNumber, F_OIDEQ,
11519 ObjectIdGetDatum(conoid));
11521 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11522 NULL, 1, &key);
11523 while ((trigtup = systable_getnext(scan)) != NULL)
11525 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11527 if (trgform->tgconstrrelid != conrelid)
11528 continue;
11529 if (trgform->tgrelid != confrelid)
11530 continue;
11531 /* Only ever look at "action" triggers on the PK side. */
11532 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11533 continue;
11534 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11536 Assert(*deleteTriggerOid == InvalidOid);
11537 *deleteTriggerOid = trgform->oid;
11539 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11541 Assert(*updateTriggerOid == InvalidOid);
11542 *updateTriggerOid = trgform->oid;
11544 #ifndef USE_ASSERT_CHECKING
11545 /* In an assert-enabled build, continue looking to find duplicates */
11546 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11547 break;
11548 #endif
11551 if (!OidIsValid(*deleteTriggerOid))
11552 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11553 conoid);
11554 if (!OidIsValid(*updateTriggerOid))
11555 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11556 conoid);
11558 systable_endscan(scan);
11562 * GetForeignKeyCheckTriggers
11563 * Returns insert and update "check" triggers of the given relation
11564 * belonging to the given constraint
11566 static void
11567 GetForeignKeyCheckTriggers(Relation trigrel,
11568 Oid conoid, Oid confrelid, Oid conrelid,
11569 Oid *insertTriggerOid,
11570 Oid *updateTriggerOid)
11572 ScanKeyData key;
11573 SysScanDesc scan;
11574 HeapTuple trigtup;
11576 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11577 ScanKeyInit(&key,
11578 Anum_pg_trigger_tgconstraint,
11579 BTEqualStrategyNumber, F_OIDEQ,
11580 ObjectIdGetDatum(conoid));
11582 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11583 NULL, 1, &key);
11584 while ((trigtup = systable_getnext(scan)) != NULL)
11586 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11588 if (trgform->tgconstrrelid != confrelid)
11589 continue;
11590 if (trgform->tgrelid != conrelid)
11591 continue;
11592 /* Only ever look at "check" triggers on the FK side. */
11593 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11594 continue;
11595 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11597 Assert(*insertTriggerOid == InvalidOid);
11598 *insertTriggerOid = trgform->oid;
11600 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11602 Assert(*updateTriggerOid == InvalidOid);
11603 *updateTriggerOid = trgform->oid;
11605 #ifndef USE_ASSERT_CHECKING
11606 /* In an assert-enabled build, continue looking to find duplicates. */
11607 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11608 break;
11609 #endif
11612 if (!OidIsValid(*insertTriggerOid))
11613 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11614 conoid);
11615 if (!OidIsValid(*updateTriggerOid))
11616 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11617 conoid);
11619 systable_endscan(scan);
11623 * ALTER TABLE ALTER CONSTRAINT
11625 * Update the attributes of a constraint.
11627 * Currently only works for Foreign Key constraints.
11629 * If the constraint is modified, returns its address; otherwise, return
11630 * InvalidObjectAddress.
11632 static ObjectAddress
11633 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11634 bool recursing, LOCKMODE lockmode)
11636 Constraint *cmdcon;
11637 Relation conrel;
11638 Relation tgrel;
11639 SysScanDesc scan;
11640 ScanKeyData skey[3];
11641 HeapTuple contuple;
11642 Form_pg_constraint currcon;
11643 ObjectAddress address;
11644 List *otherrelids = NIL;
11645 ListCell *lc;
11647 cmdcon = castNode(Constraint, cmd->def);
11649 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11650 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11653 * Find and check the target constraint
11655 ScanKeyInit(&skey[0],
11656 Anum_pg_constraint_conrelid,
11657 BTEqualStrategyNumber, F_OIDEQ,
11658 ObjectIdGetDatum(RelationGetRelid(rel)));
11659 ScanKeyInit(&skey[1],
11660 Anum_pg_constraint_contypid,
11661 BTEqualStrategyNumber, F_OIDEQ,
11662 ObjectIdGetDatum(InvalidOid));
11663 ScanKeyInit(&skey[2],
11664 Anum_pg_constraint_conname,
11665 BTEqualStrategyNumber, F_NAMEEQ,
11666 CStringGetDatum(cmdcon->conname));
11667 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11668 true, NULL, 3, skey);
11670 /* There can be at most one matching row */
11671 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11672 ereport(ERROR,
11673 (errcode(ERRCODE_UNDEFINED_OBJECT),
11674 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11675 cmdcon->conname, RelationGetRelationName(rel))));
11677 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11678 if (currcon->contype != CONSTRAINT_FOREIGN)
11679 ereport(ERROR,
11680 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11681 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11682 cmdcon->conname, RelationGetRelationName(rel))));
11685 * If it's not the topmost constraint, raise an error.
11687 * Altering a non-topmost constraint leaves some triggers untouched, since
11688 * they are not directly connected to this constraint; also, pg_dump would
11689 * ignore the deferrability status of the individual constraint, since it
11690 * only dumps topmost constraints. Avoid these problems by refusing this
11691 * operation and telling the user to alter the parent constraint instead.
11693 if (OidIsValid(currcon->conparentid))
11695 HeapTuple tp;
11696 Oid parent = currcon->conparentid;
11697 char *ancestorname = NULL;
11698 char *ancestortable = NULL;
11700 /* Loop to find the topmost constraint */
11701 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11703 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11705 /* If no parent, this is the constraint we want */
11706 if (!OidIsValid(contup->conparentid))
11708 ancestorname = pstrdup(NameStr(contup->conname));
11709 ancestortable = get_rel_name(contup->conrelid);
11710 ReleaseSysCache(tp);
11711 break;
11714 parent = contup->conparentid;
11715 ReleaseSysCache(tp);
11718 ereport(ERROR,
11719 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11720 cmdcon->conname, RelationGetRelationName(rel)),
11721 ancestorname && ancestortable ?
11722 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11723 cmdcon->conname, ancestorname, ancestortable) : 0,
11724 errhint("You may alter the constraint it derives from instead.")));
11728 * Do the actual catalog work. We can skip changing if already in the
11729 * desired state, but not if a partitioned table: partitions need to be
11730 * processed regardless, in case they had the constraint locally changed.
11732 address = InvalidObjectAddress;
11733 if (currcon->condeferrable != cmdcon->deferrable ||
11734 currcon->condeferred != cmdcon->initdeferred ||
11735 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11737 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11738 &otherrelids, lockmode))
11739 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11743 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11744 * having the constraint itself; here we also invalidate for relations
11745 * that have any triggers that are part of the constraint.
11747 foreach(lc, otherrelids)
11748 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11750 systable_endscan(scan);
11752 table_close(tgrel, RowExclusiveLock);
11753 table_close(conrel, RowExclusiveLock);
11755 return address;
11759 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11760 * constraint is altered.
11762 * *otherrelids is appended OIDs of relations containing affected triggers.
11764 * Note that we must recurse even when the values are correct, in case
11765 * indirect descendants have had their constraints altered locally.
11766 * (This could be avoided if we forbade altering constraints in partitions
11767 * but existing releases don't do that.)
11769 static bool
11770 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11771 Relation rel, HeapTuple contuple, List **otherrelids,
11772 LOCKMODE lockmode)
11774 Form_pg_constraint currcon;
11775 Oid conoid;
11776 Oid refrelid;
11777 bool changed = false;
11779 /* since this function recurses, it could be driven to stack overflow */
11780 check_stack_depth();
11782 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11783 conoid = currcon->oid;
11784 refrelid = currcon->confrelid;
11787 * Update pg_constraint with the flags from cmdcon.
11789 * If called to modify a constraint that's already in the desired state,
11790 * silently do nothing.
11792 if (currcon->condeferrable != cmdcon->deferrable ||
11793 currcon->condeferred != cmdcon->initdeferred)
11795 HeapTuple copyTuple;
11796 Form_pg_constraint copy_con;
11797 HeapTuple tgtuple;
11798 ScanKeyData tgkey;
11799 SysScanDesc tgscan;
11801 copyTuple = heap_copytuple(contuple);
11802 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11803 copy_con->condeferrable = cmdcon->deferrable;
11804 copy_con->condeferred = cmdcon->initdeferred;
11805 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11807 InvokeObjectPostAlterHook(ConstraintRelationId,
11808 conoid, 0);
11810 heap_freetuple(copyTuple);
11811 changed = true;
11813 /* Make new constraint flags visible to others */
11814 CacheInvalidateRelcache(rel);
11817 * Now we need to update the multiple entries in pg_trigger that
11818 * implement the constraint.
11820 ScanKeyInit(&tgkey,
11821 Anum_pg_trigger_tgconstraint,
11822 BTEqualStrategyNumber, F_OIDEQ,
11823 ObjectIdGetDatum(conoid));
11824 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11825 NULL, 1, &tgkey);
11826 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11828 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11829 Form_pg_trigger copy_tg;
11830 HeapTuple tgCopyTuple;
11833 * Remember OIDs of other relation(s) involved in FK constraint.
11834 * (Note: it's likely that we could skip forcing a relcache inval
11835 * for other rels that don't have a trigger whose properties
11836 * change, but let's be conservative.)
11838 if (tgform->tgrelid != RelationGetRelid(rel))
11839 *otherrelids = list_append_unique_oid(*otherrelids,
11840 tgform->tgrelid);
11843 * Update deferrability of RI_FKey_noaction_del,
11844 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11845 * triggers, but not others; see createForeignKeyActionTriggers
11846 * and CreateFKCheckTrigger.
11848 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11849 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11850 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11851 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11852 continue;
11854 tgCopyTuple = heap_copytuple(tgtuple);
11855 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11857 copy_tg->tgdeferrable = cmdcon->deferrable;
11858 copy_tg->tginitdeferred = cmdcon->initdeferred;
11859 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11861 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11863 heap_freetuple(tgCopyTuple);
11866 systable_endscan(tgscan);
11870 * If the table at either end of the constraint is partitioned, we need to
11871 * recurse and handle every constraint that is a child of this one.
11873 * (This assumes that the recurse flag is forcibly set for partitioned
11874 * tables, and not set for legacy inheritance, though we don't check for
11875 * that here.)
11877 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11878 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11880 ScanKeyData pkey;
11881 SysScanDesc pscan;
11882 HeapTuple childtup;
11884 ScanKeyInit(&pkey,
11885 Anum_pg_constraint_conparentid,
11886 BTEqualStrategyNumber, F_OIDEQ,
11887 ObjectIdGetDatum(conoid));
11889 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11890 true, NULL, 1, &pkey);
11892 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11894 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11895 Relation childrel;
11897 childrel = table_open(childcon->conrelid, lockmode);
11898 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11899 otherrelids, lockmode);
11900 table_close(childrel, NoLock);
11903 systable_endscan(pscan);
11906 return changed;
11910 * ALTER TABLE VALIDATE CONSTRAINT
11912 * XXX The reason we handle recursion here rather than at Phase 1 is because
11913 * there's no good way to skip recursing when handling foreign keys: there is
11914 * no need to lock children in that case, yet we wouldn't be able to avoid
11915 * doing so at that level.
11917 * Return value is the address of the validated constraint. If the constraint
11918 * was already validated, InvalidObjectAddress is returned.
11920 static ObjectAddress
11921 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11922 bool recurse, bool recursing, LOCKMODE lockmode)
11924 Relation conrel;
11925 SysScanDesc scan;
11926 ScanKeyData skey[3];
11927 HeapTuple tuple;
11928 Form_pg_constraint con;
11929 ObjectAddress address;
11931 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11934 * Find and check the target constraint
11936 ScanKeyInit(&skey[0],
11937 Anum_pg_constraint_conrelid,
11938 BTEqualStrategyNumber, F_OIDEQ,
11939 ObjectIdGetDatum(RelationGetRelid(rel)));
11940 ScanKeyInit(&skey[1],
11941 Anum_pg_constraint_contypid,
11942 BTEqualStrategyNumber, F_OIDEQ,
11943 ObjectIdGetDatum(InvalidOid));
11944 ScanKeyInit(&skey[2],
11945 Anum_pg_constraint_conname,
11946 BTEqualStrategyNumber, F_NAMEEQ,
11947 CStringGetDatum(constrName));
11948 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11949 true, NULL, 3, skey);
11951 /* There can be at most one matching row */
11952 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11953 ereport(ERROR,
11954 (errcode(ERRCODE_UNDEFINED_OBJECT),
11955 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11956 constrName, RelationGetRelationName(rel))));
11958 con = (Form_pg_constraint) GETSTRUCT(tuple);
11959 if (con->contype != CONSTRAINT_FOREIGN &&
11960 con->contype != CONSTRAINT_CHECK)
11961 ereport(ERROR,
11962 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11963 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11964 constrName, RelationGetRelationName(rel))));
11966 if (!con->convalidated)
11968 AlteredTableInfo *tab;
11969 HeapTuple copyTuple;
11970 Form_pg_constraint copy_con;
11972 if (con->contype == CONSTRAINT_FOREIGN)
11974 NewConstraint *newcon;
11975 Constraint *fkconstraint;
11977 /* Queue validation for phase 3 */
11978 fkconstraint = makeNode(Constraint);
11979 /* for now this is all we need */
11980 fkconstraint->conname = constrName;
11982 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11983 newcon->name = constrName;
11984 newcon->contype = CONSTR_FOREIGN;
11985 newcon->refrelid = con->confrelid;
11986 newcon->refindid = con->conindid;
11987 newcon->conid = con->oid;
11988 newcon->qual = (Node *) fkconstraint;
11990 /* Find or create work queue entry for this table */
11991 tab = ATGetQueueEntry(wqueue, rel);
11992 tab->constraints = lappend(tab->constraints, newcon);
11995 * We disallow creating invalid foreign keys to or from
11996 * partitioned tables, so ignoring the recursion bit is okay.
11999 else if (con->contype == CONSTRAINT_CHECK)
12001 List *children = NIL;
12002 ListCell *child;
12003 NewConstraint *newcon;
12004 Datum val;
12005 char *conbin;
12008 * If we're recursing, the parent has already done this, so skip
12009 * it. Also, if the constraint is a NO INHERIT constraint, we
12010 * shouldn't try to look for it in the children.
12012 if (!recursing && !con->connoinherit)
12013 children = find_all_inheritors(RelationGetRelid(rel),
12014 lockmode, NULL);
12017 * For CHECK constraints, we must ensure that we only mark the
12018 * constraint as validated on the parent if it's already validated
12019 * on the children.
12021 * We recurse before validating on the parent, to reduce risk of
12022 * deadlocks.
12024 foreach(child, children)
12026 Oid childoid = lfirst_oid(child);
12027 Relation childrel;
12029 if (childoid == RelationGetRelid(rel))
12030 continue;
12033 * If we are told not to recurse, there had better not be any
12034 * child tables, because we can't mark the constraint on the
12035 * parent valid unless it is valid for all child tables.
12037 if (!recurse)
12038 ereport(ERROR,
12039 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12040 errmsg("constraint must be validated on child tables too")));
12042 /* find_all_inheritors already got lock */
12043 childrel = table_open(childoid, NoLock);
12045 ATExecValidateConstraint(wqueue, childrel, constrName, false,
12046 true, lockmode);
12047 table_close(childrel, NoLock);
12050 /* Queue validation for phase 3 */
12051 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12052 newcon->name = constrName;
12053 newcon->contype = CONSTR_CHECK;
12054 newcon->refrelid = InvalidOid;
12055 newcon->refindid = InvalidOid;
12056 newcon->conid = con->oid;
12058 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12059 Anum_pg_constraint_conbin);
12060 conbin = TextDatumGetCString(val);
12061 newcon->qual = (Node *) stringToNode(conbin);
12063 /* Find or create work queue entry for this table */
12064 tab = ATGetQueueEntry(wqueue, rel);
12065 tab->constraints = lappend(tab->constraints, newcon);
12068 * Invalidate relcache so that others see the new validated
12069 * constraint.
12071 CacheInvalidateRelcache(rel);
12075 * Now update the catalog, while we have the door open.
12077 copyTuple = heap_copytuple(tuple);
12078 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12079 copy_con->convalidated = true;
12080 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12082 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12084 heap_freetuple(copyTuple);
12086 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12088 else
12089 address = InvalidObjectAddress; /* already validated */
12091 systable_endscan(scan);
12093 table_close(conrel, RowExclusiveLock);
12095 return address;
12100 * transformColumnNameList - transform list of column names
12102 * Lookup each name and return its attnum and, optionally, type OID
12104 * Note: the name of this function suggests that it's general-purpose,
12105 * but actually it's only used to look up names appearing in foreign-key
12106 * clauses. The error messages would need work to use it in other cases,
12107 * and perhaps the validity checks as well.
12109 static int
12110 transformColumnNameList(Oid relId, List *colList,
12111 int16 *attnums, Oid *atttypids)
12113 ListCell *l;
12114 int attnum;
12116 attnum = 0;
12117 foreach(l, colList)
12119 char *attname = strVal(lfirst(l));
12120 HeapTuple atttuple;
12121 Form_pg_attribute attform;
12123 atttuple = SearchSysCacheAttName(relId, attname);
12124 if (!HeapTupleIsValid(atttuple))
12125 ereport(ERROR,
12126 (errcode(ERRCODE_UNDEFINED_COLUMN),
12127 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12128 attname)));
12129 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12130 if (attform->attnum < 0)
12131 ereport(ERROR,
12132 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12133 errmsg("system columns cannot be used in foreign keys")));
12134 if (attnum >= INDEX_MAX_KEYS)
12135 ereport(ERROR,
12136 (errcode(ERRCODE_TOO_MANY_COLUMNS),
12137 errmsg("cannot have more than %d keys in a foreign key",
12138 INDEX_MAX_KEYS)));
12139 attnums[attnum] = attform->attnum;
12140 if (atttypids != NULL)
12141 atttypids[attnum] = attform->atttypid;
12142 ReleaseSysCache(atttuple);
12143 attnum++;
12146 return attnum;
12150 * transformFkeyGetPrimaryKey -
12152 * Look up the names, attnums, and types of the primary key attributes
12153 * for the pkrel. Also return the index OID and index opclasses of the
12154 * index supporting the primary key. Also return whether the index has
12155 * WITHOUT OVERLAPS.
12157 * All parameters except pkrel are output parameters. Also, the function
12158 * return value is the number of attributes in the primary key.
12160 * Used when the column list in the REFERENCES specification is omitted.
12162 static int
12163 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12164 List **attnamelist,
12165 int16 *attnums, Oid *atttypids,
12166 Oid *opclasses, bool *pk_has_without_overlaps)
12168 List *indexoidlist;
12169 ListCell *indexoidscan;
12170 HeapTuple indexTuple = NULL;
12171 Form_pg_index indexStruct = NULL;
12172 Datum indclassDatum;
12173 oidvector *indclass;
12174 int i;
12177 * Get the list of index OIDs for the table from the relcache, and look up
12178 * each one in the pg_index syscache until we find one marked primary key
12179 * (hopefully there isn't more than one such). Insist it's valid, too.
12181 *indexOid = InvalidOid;
12183 indexoidlist = RelationGetIndexList(pkrel);
12185 foreach(indexoidscan, indexoidlist)
12187 Oid indexoid = lfirst_oid(indexoidscan);
12189 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12190 if (!HeapTupleIsValid(indexTuple))
12191 elog(ERROR, "cache lookup failed for index %u", indexoid);
12192 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12193 if (indexStruct->indisprimary && indexStruct->indisvalid)
12196 * Refuse to use a deferrable primary key. This is per SQL spec,
12197 * and there would be a lot of interesting semantic problems if we
12198 * tried to allow it.
12200 if (!indexStruct->indimmediate)
12201 ereport(ERROR,
12202 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12203 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12204 RelationGetRelationName(pkrel))));
12206 *indexOid = indexoid;
12207 break;
12209 ReleaseSysCache(indexTuple);
12212 list_free(indexoidlist);
12215 * Check that we found it
12217 if (!OidIsValid(*indexOid))
12218 ereport(ERROR,
12219 (errcode(ERRCODE_UNDEFINED_OBJECT),
12220 errmsg("there is no primary key for referenced table \"%s\"",
12221 RelationGetRelationName(pkrel))));
12223 /* Must get indclass the hard way */
12224 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12225 Anum_pg_index_indclass);
12226 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12229 * Now build the list of PK attributes from the indkey definition (we
12230 * assume a primary key cannot have expressional elements)
12232 *attnamelist = NIL;
12233 for (i = 0; i < indexStruct->indnkeyatts; i++)
12235 int pkattno = indexStruct->indkey.values[i];
12237 attnums[i] = pkattno;
12238 atttypids[i] = attnumTypeId(pkrel, pkattno);
12239 opclasses[i] = indclass->values[i];
12240 *attnamelist = lappend(*attnamelist,
12241 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12244 *pk_has_without_overlaps = indexStruct->indisexclusion;
12246 ReleaseSysCache(indexTuple);
12248 return i;
12252 * transformFkeyCheckAttrs -
12254 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12255 * reference as part of a foreign key constraint.
12257 * Returns the OID of the unique index supporting the constraint and
12258 * populates the caller-provided 'opclasses' array with the opclasses
12259 * associated with the index columns. Also sets whether the index
12260 * uses WITHOUT OVERLAPS.
12262 * Raises an ERROR on validation failure.
12264 static Oid
12265 transformFkeyCheckAttrs(Relation pkrel,
12266 int numattrs, int16 *attnums,
12267 bool with_period, Oid *opclasses,
12268 bool *pk_has_without_overlaps)
12270 Oid indexoid = InvalidOid;
12271 bool found = false;
12272 bool found_deferrable = false;
12273 List *indexoidlist;
12274 ListCell *indexoidscan;
12275 int i,
12279 * Reject duplicate appearances of columns in the referenced-columns list.
12280 * Such a case is forbidden by the SQL standard, and even if we thought it
12281 * useful to allow it, there would be ambiguity about how to match the
12282 * list to unique indexes (in particular, it'd be unclear which index
12283 * opclass goes with which FK column).
12285 for (i = 0; i < numattrs; i++)
12287 for (j = i + 1; j < numattrs; j++)
12289 if (attnums[i] == attnums[j])
12290 ereport(ERROR,
12291 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12292 errmsg("foreign key referenced-columns list must not contain duplicates")));
12297 * Get the list of index OIDs for the table from the relcache, and look up
12298 * each one in the pg_index syscache, and match unique indexes to the list
12299 * of attnums we are given.
12301 indexoidlist = RelationGetIndexList(pkrel);
12303 foreach(indexoidscan, indexoidlist)
12305 HeapTuple indexTuple;
12306 Form_pg_index indexStruct;
12308 indexoid = lfirst_oid(indexoidscan);
12309 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12310 if (!HeapTupleIsValid(indexTuple))
12311 elog(ERROR, "cache lookup failed for index %u", indexoid);
12312 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12315 * Must have the right number of columns; must be unique (or if
12316 * temporal then exclusion instead) and not a partial index; forget it
12317 * if there are any expressions, too. Invalid indexes are out as well.
12319 if (indexStruct->indnkeyatts == numattrs &&
12320 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12321 indexStruct->indisvalid &&
12322 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12323 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12325 Datum indclassDatum;
12326 oidvector *indclass;
12328 /* Must get indclass the hard way */
12329 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12330 Anum_pg_index_indclass);
12331 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12334 * The given attnum list may match the index columns in any order.
12335 * Check for a match, and extract the appropriate opclasses while
12336 * we're at it.
12338 * We know that attnums[] is duplicate-free per the test at the
12339 * start of this function, and we checked above that the number of
12340 * index columns agrees, so if we find a match for each attnums[]
12341 * entry then we must have a one-to-one match in some order.
12343 for (i = 0; i < numattrs; i++)
12345 found = false;
12346 for (j = 0; j < numattrs; j++)
12348 if (attnums[i] == indexStruct->indkey.values[j])
12350 opclasses[i] = indclass->values[j];
12351 found = true;
12352 break;
12355 if (!found)
12356 break;
12358 /* The last attribute in the index must be the PERIOD FK part */
12359 if (found && with_period)
12361 int16 periodattnum = attnums[numattrs - 1];
12363 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12367 * Refuse to use a deferrable unique/primary key. This is per SQL
12368 * spec, and there would be a lot of interesting semantic problems
12369 * if we tried to allow it.
12371 if (found && !indexStruct->indimmediate)
12374 * Remember that we found an otherwise matching index, so that
12375 * we can generate a more appropriate error message.
12377 found_deferrable = true;
12378 found = false;
12381 /* We need to know whether the index has WITHOUT OVERLAPS */
12382 if (found)
12383 *pk_has_without_overlaps = indexStruct->indisexclusion;
12385 ReleaseSysCache(indexTuple);
12386 if (found)
12387 break;
12390 if (!found)
12392 if (found_deferrable)
12393 ereport(ERROR,
12394 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12395 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12396 RelationGetRelationName(pkrel))));
12397 else
12398 ereport(ERROR,
12399 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12400 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12401 RelationGetRelationName(pkrel))));
12404 list_free(indexoidlist);
12406 return indexoid;
12410 * findFkeyCast -
12412 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12413 * Caller has equal regard for binary coercibility and for an exact match.
12415 static CoercionPathType
12416 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12418 CoercionPathType ret;
12420 if (targetTypeId == sourceTypeId)
12422 ret = COERCION_PATH_RELABELTYPE;
12423 *funcid = InvalidOid;
12425 else
12427 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12428 COERCION_IMPLICIT, funcid);
12429 if (ret == COERCION_PATH_NONE)
12430 /* A previously-relied-upon cast is now gone. */
12431 elog(ERROR, "could not find cast from %u to %u",
12432 sourceTypeId, targetTypeId);
12435 return ret;
12439 * Permissions checks on the referenced table for ADD FOREIGN KEY
12441 * Note: we have already checked that the user owns the referencing table,
12442 * else we'd have failed much earlier; no additional checks are needed for it.
12444 static void
12445 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12447 Oid roleid = GetUserId();
12448 AclResult aclresult;
12449 int i;
12451 /* Okay if we have relation-level REFERENCES permission */
12452 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12453 ACL_REFERENCES);
12454 if (aclresult == ACLCHECK_OK)
12455 return;
12456 /* Else we must have REFERENCES on each column */
12457 for (i = 0; i < natts; i++)
12459 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12460 roleid, ACL_REFERENCES);
12461 if (aclresult != ACLCHECK_OK)
12462 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12463 RelationGetRelationName(rel));
12468 * Scan the existing rows in a table to verify they meet a proposed FK
12469 * constraint.
12471 * Caller must have opened and locked both relations appropriately.
12473 static void
12474 validateForeignKeyConstraint(char *conname,
12475 Relation rel,
12476 Relation pkrel,
12477 Oid pkindOid,
12478 Oid constraintOid,
12479 bool hasperiod)
12481 TupleTableSlot *slot;
12482 TableScanDesc scan;
12483 Trigger trig = {0};
12484 Snapshot snapshot;
12485 MemoryContext oldcxt;
12486 MemoryContext perTupCxt;
12488 ereport(DEBUG1,
12489 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12492 * Build a trigger call structure; we'll need it either way.
12494 trig.tgoid = InvalidOid;
12495 trig.tgname = conname;
12496 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12497 trig.tgisinternal = true;
12498 trig.tgconstrrelid = RelationGetRelid(pkrel);
12499 trig.tgconstrindid = pkindOid;
12500 trig.tgconstraint = constraintOid;
12501 trig.tgdeferrable = false;
12502 trig.tginitdeferred = false;
12503 /* we needn't fill in remaining fields */
12506 * See if we can do it with a single LEFT JOIN query. A false result
12507 * indicates we must proceed with the fire-the-trigger method. We can't do
12508 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12509 * left joins.
12511 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12512 return;
12515 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12516 * if that tuple had just been inserted. If any of those fail, it should
12517 * ereport(ERROR) and that's that.
12519 snapshot = RegisterSnapshot(GetLatestSnapshot());
12520 slot = table_slot_create(rel, NULL);
12521 scan = table_beginscan(rel, snapshot, 0, NULL);
12523 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12524 "validateForeignKeyConstraint",
12525 ALLOCSET_SMALL_SIZES);
12526 oldcxt = MemoryContextSwitchTo(perTupCxt);
12528 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12530 LOCAL_FCINFO(fcinfo, 0);
12531 TriggerData trigdata = {0};
12533 CHECK_FOR_INTERRUPTS();
12536 * Make a call to the trigger function
12538 * No parameters are passed, but we do set a context
12540 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12543 * We assume RI_FKey_check_ins won't look at flinfo...
12545 trigdata.type = T_TriggerData;
12546 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12547 trigdata.tg_relation = rel;
12548 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12549 trigdata.tg_trigslot = slot;
12550 trigdata.tg_trigger = &trig;
12552 fcinfo->context = (Node *) &trigdata;
12554 RI_FKey_check_ins(fcinfo);
12556 MemoryContextReset(perTupCxt);
12559 MemoryContextSwitchTo(oldcxt);
12560 MemoryContextDelete(perTupCxt);
12561 table_endscan(scan);
12562 UnregisterSnapshot(snapshot);
12563 ExecDropSingleTupleTableSlot(slot);
12567 * CreateFKCheckTrigger
12568 * Creates the insert (on_insert=true) or update "check" trigger that
12569 * implements a given foreign key
12571 * Returns the OID of the so created trigger.
12573 static Oid
12574 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12575 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12576 bool on_insert)
12578 ObjectAddress trigAddress;
12579 CreateTrigStmt *fk_trigger;
12582 * Note: for a self-referential FK (referencing and referenced tables are
12583 * the same), it is important that the ON UPDATE action fires before the
12584 * CHECK action, since both triggers will fire on the same row during an
12585 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12586 * state of the row. Triggers fire in name order, so we ensure this by
12587 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12588 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12590 fk_trigger = makeNode(CreateTrigStmt);
12591 fk_trigger->replace = false;
12592 fk_trigger->isconstraint = true;
12593 fk_trigger->trigname = "RI_ConstraintTrigger_c";
12594 fk_trigger->relation = NULL;
12596 /* Either ON INSERT or ON UPDATE */
12597 if (on_insert)
12599 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12600 fk_trigger->events = TRIGGER_TYPE_INSERT;
12602 else
12604 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12605 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12608 fk_trigger->args = NIL;
12609 fk_trigger->row = true;
12610 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12611 fk_trigger->columns = NIL;
12612 fk_trigger->whenClause = NULL;
12613 fk_trigger->transitionRels = NIL;
12614 fk_trigger->deferrable = fkconstraint->deferrable;
12615 fk_trigger->initdeferred = fkconstraint->initdeferred;
12616 fk_trigger->constrrel = NULL;
12618 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12619 constraintOid, indexOid, InvalidOid,
12620 parentTrigOid, NULL, true, false);
12622 /* Make changes-so-far visible */
12623 CommandCounterIncrement();
12625 return trigAddress.objectId;
12629 * createForeignKeyActionTriggers
12630 * Create the referenced-side "action" triggers that implement a foreign
12631 * key.
12633 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12634 * *updateTrigOid.
12636 static void
12637 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12638 Oid constraintOid, Oid indexOid,
12639 Oid parentDelTrigger, Oid parentUpdTrigger,
12640 Oid *deleteTrigOid, Oid *updateTrigOid)
12642 CreateTrigStmt *fk_trigger;
12643 ObjectAddress trigAddress;
12646 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12647 * DELETE action on the referenced table.
12649 fk_trigger = makeNode(CreateTrigStmt);
12650 fk_trigger->replace = false;
12651 fk_trigger->isconstraint = true;
12652 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12653 fk_trigger->relation = NULL;
12654 fk_trigger->args = NIL;
12655 fk_trigger->row = true;
12656 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12657 fk_trigger->events = TRIGGER_TYPE_DELETE;
12658 fk_trigger->columns = NIL;
12659 fk_trigger->whenClause = NULL;
12660 fk_trigger->transitionRels = NIL;
12661 fk_trigger->constrrel = NULL;
12663 switch (fkconstraint->fk_del_action)
12665 case FKCONSTR_ACTION_NOACTION:
12666 fk_trigger->deferrable = fkconstraint->deferrable;
12667 fk_trigger->initdeferred = fkconstraint->initdeferred;
12668 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12669 break;
12670 case FKCONSTR_ACTION_RESTRICT:
12671 fk_trigger->deferrable = false;
12672 fk_trigger->initdeferred = false;
12673 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12674 break;
12675 case FKCONSTR_ACTION_CASCADE:
12676 fk_trigger->deferrable = false;
12677 fk_trigger->initdeferred = false;
12678 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12679 break;
12680 case FKCONSTR_ACTION_SETNULL:
12681 fk_trigger->deferrable = false;
12682 fk_trigger->initdeferred = false;
12683 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12684 break;
12685 case FKCONSTR_ACTION_SETDEFAULT:
12686 fk_trigger->deferrable = false;
12687 fk_trigger->initdeferred = false;
12688 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12689 break;
12690 default:
12691 elog(ERROR, "unrecognized FK action type: %d",
12692 (int) fkconstraint->fk_del_action);
12693 break;
12696 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12697 RelationGetRelid(rel),
12698 constraintOid, indexOid, InvalidOid,
12699 parentDelTrigger, NULL, true, false);
12700 if (deleteTrigOid)
12701 *deleteTrigOid = trigAddress.objectId;
12703 /* Make changes-so-far visible */
12704 CommandCounterIncrement();
12707 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12708 * UPDATE action on the referenced table.
12710 fk_trigger = makeNode(CreateTrigStmt);
12711 fk_trigger->replace = false;
12712 fk_trigger->isconstraint = true;
12713 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12714 fk_trigger->relation = NULL;
12715 fk_trigger->args = NIL;
12716 fk_trigger->row = true;
12717 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12718 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12719 fk_trigger->columns = NIL;
12720 fk_trigger->whenClause = NULL;
12721 fk_trigger->transitionRels = NIL;
12722 fk_trigger->constrrel = NULL;
12724 switch (fkconstraint->fk_upd_action)
12726 case FKCONSTR_ACTION_NOACTION:
12727 fk_trigger->deferrable = fkconstraint->deferrable;
12728 fk_trigger->initdeferred = fkconstraint->initdeferred;
12729 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12730 break;
12731 case FKCONSTR_ACTION_RESTRICT:
12732 fk_trigger->deferrable = false;
12733 fk_trigger->initdeferred = false;
12734 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12735 break;
12736 case FKCONSTR_ACTION_CASCADE:
12737 fk_trigger->deferrable = false;
12738 fk_trigger->initdeferred = false;
12739 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12740 break;
12741 case FKCONSTR_ACTION_SETNULL:
12742 fk_trigger->deferrable = false;
12743 fk_trigger->initdeferred = false;
12744 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12745 break;
12746 case FKCONSTR_ACTION_SETDEFAULT:
12747 fk_trigger->deferrable = false;
12748 fk_trigger->initdeferred = false;
12749 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12750 break;
12751 default:
12752 elog(ERROR, "unrecognized FK action type: %d",
12753 (int) fkconstraint->fk_upd_action);
12754 break;
12757 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12758 RelationGetRelid(rel),
12759 constraintOid, indexOid, InvalidOid,
12760 parentUpdTrigger, NULL, true, false);
12761 if (updateTrigOid)
12762 *updateTrigOid = trigAddress.objectId;
12766 * createForeignKeyCheckTriggers
12767 * Create the referencing-side "check" triggers that implement a foreign
12768 * key.
12770 * Returns the OIDs of the so created triggers in *insertTrigOid and
12771 * *updateTrigOid.
12773 static void
12774 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12775 Constraint *fkconstraint, Oid constraintOid,
12776 Oid indexOid,
12777 Oid parentInsTrigger, Oid parentUpdTrigger,
12778 Oid *insertTrigOid, Oid *updateTrigOid)
12780 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12781 constraintOid, indexOid,
12782 parentInsTrigger, true);
12783 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12784 constraintOid, indexOid,
12785 parentUpdTrigger, false);
12789 * ALTER TABLE DROP CONSTRAINT
12791 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12793 static void
12794 ATExecDropConstraint(Relation rel, const char *constrName,
12795 DropBehavior behavior, bool recurse,
12796 bool missing_ok, LOCKMODE lockmode)
12798 Relation conrel;
12799 SysScanDesc scan;
12800 ScanKeyData skey[3];
12801 HeapTuple tuple;
12802 bool found = false;
12804 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12807 * Find and drop the target constraint
12809 ScanKeyInit(&skey[0],
12810 Anum_pg_constraint_conrelid,
12811 BTEqualStrategyNumber, F_OIDEQ,
12812 ObjectIdGetDatum(RelationGetRelid(rel)));
12813 ScanKeyInit(&skey[1],
12814 Anum_pg_constraint_contypid,
12815 BTEqualStrategyNumber, F_OIDEQ,
12816 ObjectIdGetDatum(InvalidOid));
12817 ScanKeyInit(&skey[2],
12818 Anum_pg_constraint_conname,
12819 BTEqualStrategyNumber, F_NAMEEQ,
12820 CStringGetDatum(constrName));
12821 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12822 true, NULL, 3, skey);
12824 /* There can be at most one matching row */
12825 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12827 List *readyRels = NIL;
12829 dropconstraint_internal(rel, tuple, behavior, recurse, false,
12830 missing_ok, &readyRels, lockmode);
12831 found = true;
12834 systable_endscan(scan);
12836 if (!found)
12838 if (!missing_ok)
12839 ereport(ERROR,
12840 errcode(ERRCODE_UNDEFINED_OBJECT),
12841 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12842 constrName, RelationGetRelationName(rel)));
12843 else
12844 ereport(NOTICE,
12845 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12846 constrName, RelationGetRelationName(rel)));
12849 table_close(conrel, RowExclusiveLock);
12853 * Remove a constraint, using its pg_constraint tuple
12855 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12856 * DROP NOT NULL.
12858 * Returns the address of the constraint being removed.
12860 static ObjectAddress
12861 dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12862 bool recurse, bool recursing, bool missing_ok, List **readyRels,
12863 LOCKMODE lockmode)
12865 Relation conrel;
12866 Form_pg_constraint con;
12867 ObjectAddress conobj;
12868 List *children;
12869 ListCell *child;
12870 bool is_no_inherit_constraint = false;
12871 bool dropping_pk = false;
12872 char *constrName;
12873 List *unconstrained_cols = NIL;
12874 char *colname;
12876 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12877 return InvalidObjectAddress;
12878 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12880 /* Guard against stack overflow due to overly deep inheritance tree. */
12881 check_stack_depth();
12883 /* At top level, permission check was done in ATPrepCmd, else do it */
12884 if (recursing)
12885 ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12887 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12889 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12890 constrName = NameStr(con->conname);
12892 /* Don't allow drop of inherited constraints */
12893 if (con->coninhcount > 0 && !recursing)
12894 ereport(ERROR,
12895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12896 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12897 constrName, RelationGetRelationName(rel))));
12900 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
12901 * more checks and actions below, so obtain the list of columns that are
12902 * constrained by the constraint being dropped.
12904 if (con->contype == CONSTRAINT_NOTNULL)
12906 AttrNumber colnum = extractNotNullColumn(constraintTup);
12908 if (colnum != InvalidAttrNumber)
12909 unconstrained_cols = list_make1_int(colnum);
12911 else if (con->contype == CONSTRAINT_PRIMARY)
12913 Datum adatum;
12914 ArrayType *arr;
12915 int numkeys;
12916 bool isNull;
12917 int16 *attnums;
12919 dropping_pk = true;
12921 adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
12922 RelationGetDescr(conrel), &isNull);
12923 if (isNull)
12924 elog(ERROR, "null conkey for constraint %u", con->oid);
12925 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
12926 numkeys = ARR_DIMS(arr)[0];
12927 if (ARR_NDIM(arr) != 1 ||
12928 numkeys < 0 ||
12929 ARR_HASNULL(arr) ||
12930 ARR_ELEMTYPE(arr) != INT2OID)
12931 elog(ERROR, "conkey is not a 1-D smallint array");
12932 attnums = (int16 *) ARR_DATA_PTR(arr);
12934 for (int i = 0; i < numkeys; i++)
12935 unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
12938 is_no_inherit_constraint = con->connoinherit;
12941 * If it's a foreign-key constraint, we'd better lock the referenced table
12942 * and check that that's not in use, just as we've already done for the
12943 * constrained table (else we might, eg, be dropping a trigger that has
12944 * unfired events). But we can/must skip that in the self-referential
12945 * case.
12947 if (con->contype == CONSTRAINT_FOREIGN &&
12948 con->confrelid != RelationGetRelid(rel))
12950 Relation frel;
12952 /* Must match lock taken by RemoveTriggerById: */
12953 frel = table_open(con->confrelid, AccessExclusiveLock);
12954 CheckTableNotInUse(frel, "ALTER TABLE");
12955 table_close(frel, NoLock);
12959 * Perform the actual constraint deletion
12961 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12962 performDeletion(&conobj, behavior, 0);
12965 * If this was a NOT NULL or the primary key, the constrained columns must
12966 * have had pg_attribute.attnotnull set. See if we need to reset it, and
12967 * do so.
12969 if (unconstrained_cols)
12971 Relation attrel;
12972 Bitmapset *pkcols;
12973 Bitmapset *ircols;
12974 ListCell *lc;
12976 /* Make the above deletion visible */
12977 CommandCounterIncrement();
12979 attrel = table_open(AttributeRelationId, RowExclusiveLock);
12982 * We want to test columns for their presence in the primary key, but
12983 * only if we're not dropping it.
12985 pkcols = dropping_pk ? NULL :
12986 RelationGetIndexAttrBitmap(rel,
12987 INDEX_ATTR_BITMAP_PRIMARY_KEY);
12988 ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
12990 foreach(lc, unconstrained_cols)
12992 AttrNumber attnum = lfirst_int(lc);
12993 HeapTuple atttup;
12994 HeapTuple contup;
12995 Form_pg_attribute attForm;
12998 * Obtain pg_attribute tuple and verify conditions on it. We use
12999 * a copy we can scribble on.
13001 atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13002 if (!HeapTupleIsValid(atttup))
13003 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13004 attnum, RelationGetRelid(rel));
13005 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13008 * Since the above deletion has been made visible, we can now
13009 * search for any remaining constraints on this column (or these
13010 * columns, in the case we're dropping a multicol primary key.)
13011 * Then, verify whether any further NOT NULL or primary key
13012 * exists, and reset attnotnull if none.
13014 * However, if this is a generated identity column, abort the
13015 * whole thing with a specific error message, because the
13016 * constraint is required in that case.
13018 contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
13019 if (contup ||
13020 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
13021 pkcols))
13022 continue;
13025 * It's not valid to drop the not-null constraint for a GENERATED
13026 * AS IDENTITY column.
13028 if (attForm->attidentity)
13029 ereport(ERROR,
13030 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13031 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13032 get_attname(RelationGetRelid(rel), attnum,
13033 false),
13034 RelationGetRelationName(rel)));
13037 * It's not valid to drop the not-null constraint for a column in
13038 * the replica identity index, either. (FULL is not affected.)
13040 if (bms_is_member(lfirst_int(lc) - FirstLowInvalidHeapAttributeNumber, ircols))
13041 ereport(ERROR,
13042 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13043 errmsg("column \"%s\" is in index used as replica identity",
13044 get_attname(RelationGetRelid(rel), lfirst_int(lc), false)));
13046 /* Reset attnotnull */
13047 if (attForm->attnotnull)
13049 attForm->attnotnull = false;
13050 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13053 table_close(attrel, RowExclusiveLock);
13057 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13058 * are dropped via the dependency mechanism, so we're done here.
13060 if (con->contype != CONSTRAINT_CHECK &&
13061 con->contype != CONSTRAINT_NOTNULL &&
13062 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13064 table_close(conrel, RowExclusiveLock);
13065 return conobj;
13069 * Propagate to children as appropriate. Unlike most other ALTER
13070 * routines, we have to do this one level of recursion at a time; we can't
13071 * use find_all_inheritors to do it in one pass.
13073 if (!is_no_inherit_constraint)
13074 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13075 else
13076 children = NIL;
13079 * For a partitioned table, if partitions exist and we are told not to
13080 * recurse, it's a user error. It doesn't make sense to have a constraint
13081 * be defined only on the parent, especially if it's a partitioned table.
13083 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
13084 children != NIL && !recurse)
13085 ereport(ERROR,
13086 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13087 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13088 errhint("Do not specify the ONLY keyword.")));
13090 /* For not-null constraints we recurse by column name */
13091 if (con->contype == CONSTRAINT_NOTNULL)
13092 colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
13093 linitial_int(unconstrained_cols) - 1)->attname);
13094 else
13095 colname = NULL; /* keep compiler quiet */
13097 foreach(child, children)
13099 Oid childrelid = lfirst_oid(child);
13100 Relation childrel;
13101 HeapTuple tuple;
13102 Form_pg_constraint childcon;
13104 if (list_member_oid(*readyRels, childrelid))
13105 continue; /* child already processed */
13107 /* find_inheritance_children already got lock */
13108 childrel = table_open(childrelid, NoLock);
13109 CheckTableNotInUse(childrel, "ALTER TABLE");
13112 * We search for not-null constraint by column number, and other
13113 * constraints by name.
13115 if (con->contype == CONSTRAINT_NOTNULL)
13117 tuple = findNotNullConstraint(childrelid, colname);
13118 if (!HeapTupleIsValid(tuple))
13119 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13120 colname, RelationGetRelid(childrel));
13122 else
13124 SysScanDesc scan;
13125 ScanKeyData skey[3];
13127 ScanKeyInit(&skey[0],
13128 Anum_pg_constraint_conrelid,
13129 BTEqualStrategyNumber, F_OIDEQ,
13130 ObjectIdGetDatum(childrelid));
13131 ScanKeyInit(&skey[1],
13132 Anum_pg_constraint_contypid,
13133 BTEqualStrategyNumber, F_OIDEQ,
13134 ObjectIdGetDatum(InvalidOid));
13135 ScanKeyInit(&skey[2],
13136 Anum_pg_constraint_conname,
13137 BTEqualStrategyNumber, F_NAMEEQ,
13138 CStringGetDatum(constrName));
13139 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13140 true, NULL, 3, skey);
13141 /* There can only be one, so no need to loop */
13142 tuple = systable_getnext(scan);
13143 if (!HeapTupleIsValid(tuple))
13144 ereport(ERROR,
13145 (errcode(ERRCODE_UNDEFINED_OBJECT),
13146 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13147 constrName,
13148 RelationGetRelationName(childrel))));
13149 tuple = heap_copytuple(tuple);
13150 systable_endscan(scan);
13153 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13155 /* Right now only CHECK and not-null constraints can be inherited */
13156 if (childcon->contype != CONSTRAINT_CHECK &&
13157 childcon->contype != CONSTRAINT_NOTNULL)
13158 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13160 if (childcon->coninhcount <= 0) /* shouldn't happen */
13161 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13162 childrelid, NameStr(childcon->conname));
13164 if (recurse)
13167 * If the child constraint has other definition sources, just
13168 * decrement its inheritance count; if not, recurse to delete it.
13170 if (childcon->coninhcount == 1 && !childcon->conislocal)
13172 /* Time to delete this child constraint, too */
13173 dropconstraint_internal(childrel, tuple, behavior,
13174 recurse, true, missing_ok, readyRels,
13175 lockmode);
13177 else
13179 /* Child constraint must survive my deletion */
13180 childcon->coninhcount--;
13181 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13183 /* Make update visible */
13184 CommandCounterIncrement();
13187 else
13190 * If we were told to drop ONLY in this table (no recursion) and
13191 * there are no further parents for this constraint, we need to
13192 * mark the inheritors' constraints as locally defined rather than
13193 * inherited.
13195 childcon->coninhcount--;
13196 if (childcon->coninhcount == 0)
13197 childcon->conislocal = true;
13199 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13201 /* Make update visible */
13202 CommandCounterIncrement();
13205 heap_freetuple(tuple);
13207 table_close(childrel, NoLock);
13211 * In addition, when dropping a primary key from a legacy-inheritance
13212 * parent table, we must recurse to children to mark the corresponding NOT
13213 * NULL constraint as no longer inherited, or drop it if this its last
13214 * reference.
13216 if (con->contype == CONSTRAINT_PRIMARY &&
13217 rel->rd_rel->relkind == RELKIND_RELATION &&
13218 rel->rd_rel->relhassubclass)
13220 List *colnames = NIL;
13221 ListCell *lc;
13222 List *pkready = NIL;
13225 * Because primary keys are always marked as NO INHERIT, we don't have
13226 * a list of children yet, so obtain one now.
13228 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13231 * Find out the list of column names to process. Fortunately, we
13232 * already have the list of column numbers.
13234 foreach(lc, unconstrained_cols)
13236 colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
13237 lfirst_int(lc), false));
13240 foreach(child, children)
13242 Oid childrelid = lfirst_oid(child);
13243 Relation childrel;
13245 if (list_member_oid(pkready, childrelid))
13246 continue; /* child already processed */
13248 /* find_inheritance_children already got lock */
13249 childrel = table_open(childrelid, NoLock);
13250 CheckTableNotInUse(childrel, "ALTER TABLE");
13252 foreach(lc, colnames)
13254 HeapTuple contup;
13255 char *colName = lfirst(lc);
13257 contup = findNotNullConstraint(childrelid, colName);
13258 if (contup == NULL)
13259 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13260 colName, RelationGetRelationName(childrel));
13262 dropconstraint_internal(childrel, contup,
13263 DROP_RESTRICT, true, true,
13264 false, &pkready,
13265 lockmode);
13266 pkready = NIL;
13269 table_close(childrel, NoLock);
13271 pkready = lappend_oid(pkready, childrelid);
13275 table_close(conrel, RowExclusiveLock);
13277 return conobj;
13281 * ALTER COLUMN TYPE
13283 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13284 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13285 * transformed (and must be, because we rely on some transformed fields).
13287 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13288 * table will be done "in parallel" during phase 3, so all the USING
13289 * expressions should be parsed assuming the original column types. Also,
13290 * this allows a USING expression to refer to a field that will be dropped.
13292 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13293 * the first two execution steps in phase 2; they must not see the effects
13294 * of any other subcommand types, since the USING expressions are parsed
13295 * against the unmodified table's state.
13297 static void
13298 ATPrepAlterColumnType(List **wqueue,
13299 AlteredTableInfo *tab, Relation rel,
13300 bool recurse, bool recursing,
13301 AlterTableCmd *cmd, LOCKMODE lockmode,
13302 AlterTableUtilityContext *context)
13304 char *colName = cmd->name;
13305 ColumnDef *def = (ColumnDef *) cmd->def;
13306 TypeName *typeName = def->typeName;
13307 Node *transform = def->cooked_default;
13308 HeapTuple tuple;
13309 Form_pg_attribute attTup;
13310 AttrNumber attnum;
13311 Oid targettype;
13312 int32 targettypmod;
13313 Oid targetcollid;
13314 NewColumnValue *newval;
13315 ParseState *pstate = make_parsestate(NULL);
13316 AclResult aclresult;
13317 bool is_expr;
13319 if (rel->rd_rel->reloftype && !recursing)
13320 ereport(ERROR,
13321 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13322 errmsg("cannot alter column type of typed table")));
13324 /* lookup the attribute so we can check inheritance status */
13325 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13326 if (!HeapTupleIsValid(tuple))
13327 ereport(ERROR,
13328 (errcode(ERRCODE_UNDEFINED_COLUMN),
13329 errmsg("column \"%s\" of relation \"%s\" does not exist",
13330 colName, RelationGetRelationName(rel))));
13331 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13332 attnum = attTup->attnum;
13334 /* Can't alter a system attribute */
13335 if (attnum <= 0)
13336 ereport(ERROR,
13337 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13338 errmsg("cannot alter system column \"%s\"",
13339 colName)));
13342 * Don't alter inherited columns. At outer level, there had better not be
13343 * any inherited definition; when recursing, we assume this was checked at
13344 * the parent level (see below).
13346 if (attTup->attinhcount > 0 && !recursing)
13347 ereport(ERROR,
13348 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13349 errmsg("cannot alter inherited column \"%s\"",
13350 colName)));
13352 /* Don't alter columns used in the partition key */
13353 if (has_partition_attrs(rel,
13354 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13355 &is_expr))
13356 ereport(ERROR,
13357 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13358 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13359 colName, RelationGetRelationName(rel))));
13361 /* Look up the target type */
13362 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13364 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13365 if (aclresult != ACLCHECK_OK)
13366 aclcheck_error_type(aclresult, targettype);
13368 /* And the collation */
13369 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13371 /* make sure datatype is legal for a column */
13372 CheckAttributeType(colName, targettype, targetcollid,
13373 list_make1_oid(rel->rd_rel->reltype),
13376 if (tab->relkind == RELKIND_RELATION ||
13377 tab->relkind == RELKIND_PARTITIONED_TABLE)
13380 * Set up an expression to transform the old data value to the new
13381 * type. If a USING option was given, use the expression as
13382 * transformed by transformAlterTableStmt, else just take the old
13383 * value and try to coerce it. We do this first so that type
13384 * incompatibility can be detected before we waste effort, and because
13385 * we need the expression to be parsed against the original table row
13386 * type.
13388 if (!transform)
13390 transform = (Node *) makeVar(1, attnum,
13391 attTup->atttypid, attTup->atttypmod,
13392 attTup->attcollation,
13396 transform = coerce_to_target_type(pstate,
13397 transform, exprType(transform),
13398 targettype, targettypmod,
13399 COERCION_ASSIGNMENT,
13400 COERCE_IMPLICIT_CAST,
13401 -1);
13402 if (transform == NULL)
13404 /* error text depends on whether USING was specified or not */
13405 if (def->cooked_default != NULL)
13406 ereport(ERROR,
13407 (errcode(ERRCODE_DATATYPE_MISMATCH),
13408 errmsg("result of USING clause for column \"%s\""
13409 " cannot be cast automatically to type %s",
13410 colName, format_type_be(targettype)),
13411 errhint("You might need to add an explicit cast.")));
13412 else
13413 ereport(ERROR,
13414 (errcode(ERRCODE_DATATYPE_MISMATCH),
13415 errmsg("column \"%s\" cannot be cast automatically to type %s",
13416 colName, format_type_be(targettype)),
13417 /* translator: USING is SQL, don't translate it */
13418 errhint("You might need to specify \"USING %s::%s\".",
13419 quote_identifier(colName),
13420 format_type_with_typemod(targettype,
13421 targettypmod))));
13424 /* Fix collations after all else */
13425 assign_expr_collations(pstate, transform);
13427 /* Plan the expr now so we can accurately assess the need to rewrite. */
13428 transform = (Node *) expression_planner((Expr *) transform);
13431 * Add a work queue item to make ATRewriteTable update the column
13432 * contents.
13434 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13435 newval->attnum = attnum;
13436 newval->expr = (Expr *) transform;
13437 newval->is_generated = false;
13439 tab->newvals = lappend(tab->newvals, newval);
13440 if (ATColumnChangeRequiresRewrite(transform, attnum))
13441 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13443 else if (transform)
13444 ereport(ERROR,
13445 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13446 errmsg("\"%s\" is not a table",
13447 RelationGetRelationName(rel))));
13449 if (!RELKIND_HAS_STORAGE(tab->relkind))
13452 * For relations without storage, do this check now. Regular tables
13453 * will check it later when the table is being rewritten.
13455 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13458 ReleaseSysCache(tuple);
13461 * Recurse manually by queueing a new command for each child, if
13462 * necessary. We cannot apply ATSimpleRecursion here because we need to
13463 * remap attribute numbers in the USING expression, if any.
13465 * If we are told not to recurse, there had better not be any child
13466 * tables; else the alter would put them out of step.
13468 if (recurse)
13470 Oid relid = RelationGetRelid(rel);
13471 List *child_oids,
13472 *child_numparents;
13473 ListCell *lo,
13474 *li;
13476 child_oids = find_all_inheritors(relid, lockmode,
13477 &child_numparents);
13480 * find_all_inheritors does the recursive search of the inheritance
13481 * hierarchy, so all we have to do is process all of the relids in the
13482 * list that it returns.
13484 forboth(lo, child_oids, li, child_numparents)
13486 Oid childrelid = lfirst_oid(lo);
13487 int numparents = lfirst_int(li);
13488 Relation childrel;
13489 HeapTuple childtuple;
13490 Form_pg_attribute childattTup;
13492 if (childrelid == relid)
13493 continue;
13495 /* find_all_inheritors already got lock */
13496 childrel = relation_open(childrelid, NoLock);
13497 CheckTableNotInUse(childrel, "ALTER TABLE");
13500 * Verify that the child doesn't have any inherited definitions of
13501 * this column that came from outside this inheritance hierarchy.
13502 * (renameatt makes a similar test, though in a different way
13503 * because of its different recursion mechanism.)
13505 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13506 colName);
13507 if (!HeapTupleIsValid(childtuple))
13508 ereport(ERROR,
13509 (errcode(ERRCODE_UNDEFINED_COLUMN),
13510 errmsg("column \"%s\" of relation \"%s\" does not exist",
13511 colName, RelationGetRelationName(childrel))));
13512 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13514 if (childattTup->attinhcount > numparents)
13515 ereport(ERROR,
13516 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13517 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13518 colName, RelationGetRelationName(childrel))));
13520 ReleaseSysCache(childtuple);
13523 * Remap the attribute numbers. If no USING expression was
13524 * specified, there is no need for this step.
13526 if (def->cooked_default)
13528 AttrMap *attmap;
13529 bool found_whole_row;
13531 /* create a copy to scribble on */
13532 cmd = copyObject(cmd);
13534 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13535 RelationGetDescr(rel),
13536 false);
13537 ((ColumnDef *) cmd->def)->cooked_default =
13538 map_variable_attnos(def->cooked_default,
13539 1, 0,
13540 attmap,
13541 InvalidOid, &found_whole_row);
13542 if (found_whole_row)
13543 ereport(ERROR,
13544 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13545 errmsg("cannot convert whole-row table reference"),
13546 errdetail("USING expression contains a whole-row table reference.")));
13547 pfree(attmap);
13549 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13550 relation_close(childrel, NoLock);
13553 else if (!recursing &&
13554 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13555 ereport(ERROR,
13556 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13557 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13558 colName)));
13560 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13561 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13565 * When the data type of a column is changed, a rewrite might not be required
13566 * if the new type is sufficiently identical to the old one, and the USING
13567 * clause isn't trying to insert some other value. It's safe to skip the
13568 * rewrite in these cases:
13570 * - the old type is binary coercible to the new type
13571 * - the new type is an unconstrained domain over the old type
13572 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13574 * In the case of a constrained domain, we could get by with scanning the
13575 * table and checking the constraint rather than actually rewriting it, but we
13576 * don't currently try to do that.
13578 static bool
13579 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13581 Assert(expr != NULL);
13583 for (;;)
13585 /* only one varno, so no need to check that */
13586 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13587 return false;
13588 else if (IsA(expr, RelabelType))
13589 expr = (Node *) ((RelabelType *) expr)->arg;
13590 else if (IsA(expr, CoerceToDomain))
13592 CoerceToDomain *d = (CoerceToDomain *) expr;
13594 if (DomainHasConstraints(d->resulttype))
13595 return true;
13596 expr = (Node *) d->arg;
13598 else if (IsA(expr, FuncExpr))
13600 FuncExpr *f = (FuncExpr *) expr;
13602 switch (f->funcid)
13604 case F_TIMESTAMPTZ_TIMESTAMP:
13605 case F_TIMESTAMP_TIMESTAMPTZ:
13606 if (TimestampTimestampTzRequiresRewrite())
13607 return true;
13608 else
13609 expr = linitial(f->args);
13610 break;
13611 default:
13612 return true;
13615 else
13616 return true;
13621 * ALTER COLUMN .. SET DATA TYPE
13623 * Return the address of the modified column.
13625 static ObjectAddress
13626 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13627 AlterTableCmd *cmd, LOCKMODE lockmode)
13629 char *colName = cmd->name;
13630 ColumnDef *def = (ColumnDef *) cmd->def;
13631 TypeName *typeName = def->typeName;
13632 HeapTuple heapTup;
13633 Form_pg_attribute attTup,
13634 attOldTup;
13635 AttrNumber attnum;
13636 HeapTuple typeTuple;
13637 Form_pg_type tform;
13638 Oid targettype;
13639 int32 targettypmod;
13640 Oid targetcollid;
13641 Node *defaultexpr;
13642 Relation attrelation;
13643 Relation depRel;
13644 ScanKeyData key[3];
13645 SysScanDesc scan;
13646 HeapTuple depTup;
13647 ObjectAddress address;
13650 * Clear all the missing values if we're rewriting the table, since this
13651 * renders them pointless.
13653 if (tab->rewrite)
13655 Relation newrel;
13657 newrel = table_open(RelationGetRelid(rel), NoLock);
13658 RelationClearMissing(newrel);
13659 relation_close(newrel, NoLock);
13660 /* make sure we don't conflict with later attribute modifications */
13661 CommandCounterIncrement();
13664 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13666 /* Look up the target column */
13667 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13668 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13669 ereport(ERROR,
13670 (errcode(ERRCODE_UNDEFINED_COLUMN),
13671 errmsg("column \"%s\" of relation \"%s\" does not exist",
13672 colName, RelationGetRelationName(rel))));
13673 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13674 attnum = attTup->attnum;
13675 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13677 /* Check for multiple ALTER TYPE on same column --- can't cope */
13678 if (attTup->atttypid != attOldTup->atttypid ||
13679 attTup->atttypmod != attOldTup->atttypmod)
13680 ereport(ERROR,
13681 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13682 errmsg("cannot alter type of column \"%s\" twice",
13683 colName)));
13685 /* Look up the target type (should not fail, since prep found it) */
13686 typeTuple = typenameType(NULL, typeName, &targettypmod);
13687 tform = (Form_pg_type) GETSTRUCT(typeTuple);
13688 targettype = tform->oid;
13689 /* And the collation */
13690 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13693 * If there is a default expression for the column, get it and ensure we
13694 * can coerce it to the new datatype. (We must do this before changing
13695 * the column type, because build_column_default itself will try to
13696 * coerce, and will not issue the error message we want if it fails.)
13698 * We remove any implicit coercion steps at the top level of the old
13699 * default expression; this has been agreed to satisfy the principle of
13700 * least surprise. (The conversion to the new column type should act like
13701 * it started from what the user sees as the stored expression, and the
13702 * implicit coercions aren't going to be shown.)
13704 if (attTup->atthasdef)
13706 defaultexpr = build_column_default(rel, attnum);
13707 Assert(defaultexpr);
13708 defaultexpr = strip_implicit_coercions(defaultexpr);
13709 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13710 defaultexpr, exprType(defaultexpr),
13711 targettype, targettypmod,
13712 COERCION_ASSIGNMENT,
13713 COERCE_IMPLICIT_CAST,
13714 -1);
13715 if (defaultexpr == NULL)
13717 if (attTup->attgenerated)
13718 ereport(ERROR,
13719 (errcode(ERRCODE_DATATYPE_MISMATCH),
13720 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13721 colName, format_type_be(targettype))));
13722 else
13723 ereport(ERROR,
13724 (errcode(ERRCODE_DATATYPE_MISMATCH),
13725 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13726 colName, format_type_be(targettype))));
13729 else
13730 defaultexpr = NULL;
13733 * Find everything that depends on the column (constraints, indexes, etc),
13734 * and record enough information to let us recreate the objects.
13736 * The actual recreation does not happen here, but only after we have
13737 * performed all the individual ALTER TYPE operations. We have to save
13738 * the info before executing ALTER TYPE, though, else the deparser will
13739 * get confused.
13741 RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13744 * Now scan for dependencies of this column on other things. The only
13745 * things we should find are the dependency on the column datatype and
13746 * possibly a collation dependency. Those can be removed.
13748 depRel = table_open(DependRelationId, RowExclusiveLock);
13750 ScanKeyInit(&key[0],
13751 Anum_pg_depend_classid,
13752 BTEqualStrategyNumber, F_OIDEQ,
13753 ObjectIdGetDatum(RelationRelationId));
13754 ScanKeyInit(&key[1],
13755 Anum_pg_depend_objid,
13756 BTEqualStrategyNumber, F_OIDEQ,
13757 ObjectIdGetDatum(RelationGetRelid(rel)));
13758 ScanKeyInit(&key[2],
13759 Anum_pg_depend_objsubid,
13760 BTEqualStrategyNumber, F_INT4EQ,
13761 Int32GetDatum((int32) attnum));
13763 scan = systable_beginscan(depRel, DependDependerIndexId, true,
13764 NULL, 3, key);
13766 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13768 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13769 ObjectAddress foundObject;
13771 foundObject.classId = foundDep->refclassid;
13772 foundObject.objectId = foundDep->refobjid;
13773 foundObject.objectSubId = foundDep->refobjsubid;
13775 if (foundDep->deptype != DEPENDENCY_NORMAL)
13776 elog(ERROR, "found unexpected dependency type '%c'",
13777 foundDep->deptype);
13778 if (!(foundDep->refclassid == TypeRelationId &&
13779 foundDep->refobjid == attTup->atttypid) &&
13780 !(foundDep->refclassid == CollationRelationId &&
13781 foundDep->refobjid == attTup->attcollation))
13782 elog(ERROR, "found unexpected dependency for column: %s",
13783 getObjectDescription(&foundObject, false));
13785 CatalogTupleDelete(depRel, &depTup->t_self);
13788 systable_endscan(scan);
13790 table_close(depRel, RowExclusiveLock);
13793 * Here we go --- change the recorded column type and collation. (Note
13794 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13795 * fix up the missing value if any.
13797 if (attTup->atthasmissing)
13799 Datum missingval;
13800 bool missingNull;
13802 /* if rewrite is true the missing value should already be cleared */
13803 Assert(tab->rewrite == 0);
13805 /* Get the missing value datum */
13806 missingval = heap_getattr(heapTup,
13807 Anum_pg_attribute_attmissingval,
13808 attrelation->rd_att,
13809 &missingNull);
13811 /* if it's a null array there is nothing to do */
13813 if (!missingNull)
13816 * Get the datum out of the array and repack it in a new array
13817 * built with the new type data. We assume that since the table
13818 * doesn't need rewriting, the actual Datum doesn't need to be
13819 * changed, only the array metadata.
13822 int one = 1;
13823 bool isNull;
13824 Datum valuesAtt[Natts_pg_attribute] = {0};
13825 bool nullsAtt[Natts_pg_attribute] = {0};
13826 bool replacesAtt[Natts_pg_attribute] = {0};
13827 HeapTuple newTup;
13829 missingval = array_get_element(missingval,
13831 &one,
13833 attTup->attlen,
13834 attTup->attbyval,
13835 attTup->attalign,
13836 &isNull);
13837 missingval = PointerGetDatum(construct_array(&missingval,
13839 targettype,
13840 tform->typlen,
13841 tform->typbyval,
13842 tform->typalign));
13844 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13845 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13846 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13848 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13849 valuesAtt, nullsAtt, replacesAtt);
13850 heap_freetuple(heapTup);
13851 heapTup = newTup;
13852 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13856 attTup->atttypid = targettype;
13857 attTup->atttypmod = targettypmod;
13858 attTup->attcollation = targetcollid;
13859 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13860 ereport(ERROR,
13861 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13862 errmsg("too many array dimensions"));
13863 attTup->attndims = list_length(typeName->arrayBounds);
13864 attTup->attlen = tform->typlen;
13865 attTup->attbyval = tform->typbyval;
13866 attTup->attalign = tform->typalign;
13867 attTup->attstorage = tform->typstorage;
13868 attTup->attcompression = InvalidCompressionMethod;
13870 ReleaseSysCache(typeTuple);
13872 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13874 table_close(attrelation, RowExclusiveLock);
13876 /* Install dependencies on new datatype and collation */
13877 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13878 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13881 * Drop any pg_statistic entry for the column, since it's now wrong type
13883 RemoveStatistics(RelationGetRelid(rel), attnum);
13885 InvokeObjectPostAlterHook(RelationRelationId,
13886 RelationGetRelid(rel), attnum);
13889 * Update the default, if present, by brute force --- remove and re-add
13890 * the default. Probably unsafe to take shortcuts, since the new version
13891 * may well have additional dependencies. (It's okay to do this now,
13892 * rather than after other ALTER TYPE commands, since the default won't
13893 * depend on other column types.)
13895 if (defaultexpr)
13898 * If it's a GENERATED default, drop its dependency records, in
13899 * particular its INTERNAL dependency on the column, which would
13900 * otherwise cause dependency.c to refuse to perform the deletion.
13902 if (attTup->attgenerated)
13904 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13906 if (!OidIsValid(attrdefoid))
13907 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13908 RelationGetRelid(rel), attnum);
13909 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13913 * Make updates-so-far visible, particularly the new pg_attribute row
13914 * which will be updated again.
13916 CommandCounterIncrement();
13919 * We use RESTRICT here for safety, but at present we do not expect
13920 * anything to depend on the default.
13922 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13923 true);
13925 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13928 ObjectAddressSubSet(address, RelationRelationId,
13929 RelationGetRelid(rel), attnum);
13931 /* Cleanup */
13932 heap_freetuple(heapTup);
13934 return address;
13938 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13939 * that depends on the column (constraints, indexes, etc), and record enough
13940 * information to let us recreate the objects.
13942 static void
13943 RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13944 Relation rel, AttrNumber attnum, const char *colName)
13946 Relation depRel;
13947 ScanKeyData key[3];
13948 SysScanDesc scan;
13949 HeapTuple depTup;
13951 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13953 depRel = table_open(DependRelationId, RowExclusiveLock);
13955 ScanKeyInit(&key[0],
13956 Anum_pg_depend_refclassid,
13957 BTEqualStrategyNumber, F_OIDEQ,
13958 ObjectIdGetDatum(RelationRelationId));
13959 ScanKeyInit(&key[1],
13960 Anum_pg_depend_refobjid,
13961 BTEqualStrategyNumber, F_OIDEQ,
13962 ObjectIdGetDatum(RelationGetRelid(rel)));
13963 ScanKeyInit(&key[2],
13964 Anum_pg_depend_refobjsubid,
13965 BTEqualStrategyNumber, F_INT4EQ,
13966 Int32GetDatum((int32) attnum));
13968 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13969 NULL, 3, key);
13971 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13973 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13974 ObjectAddress foundObject;
13976 foundObject.classId = foundDep->classid;
13977 foundObject.objectId = foundDep->objid;
13978 foundObject.objectSubId = foundDep->objsubid;
13980 switch (getObjectClass(&foundObject))
13982 case OCLASS_CLASS:
13984 char relKind = get_rel_relkind(foundObject.objectId);
13986 if (relKind == RELKIND_INDEX ||
13987 relKind == RELKIND_PARTITIONED_INDEX)
13989 Assert(foundObject.objectSubId == 0);
13990 RememberIndexForRebuilding(foundObject.objectId, tab);
13992 else if (relKind == RELKIND_SEQUENCE)
13995 * This must be a SERIAL column's sequence. We need
13996 * not do anything to it.
13998 Assert(foundObject.objectSubId == 0);
14000 else
14002 /* Not expecting any other direct dependencies... */
14003 elog(ERROR, "unexpected object depending on column: %s",
14004 getObjectDescription(&foundObject, false));
14006 break;
14009 case OCLASS_CONSTRAINT:
14010 Assert(foundObject.objectSubId == 0);
14011 RememberConstraintForRebuilding(foundObject.objectId, tab);
14012 break;
14014 case OCLASS_REWRITE:
14015 /* XXX someday see if we can cope with revising views */
14016 if (subtype == AT_AlterColumnType)
14017 ereport(ERROR,
14018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14019 errmsg("cannot alter type of a column used by a view or rule"),
14020 errdetail("%s depends on column \"%s\"",
14021 getObjectDescription(&foundObject, false),
14022 colName)));
14023 break;
14025 case OCLASS_TRIGGER:
14028 * A trigger can depend on a column because the column is
14029 * specified as an update target, or because the column is
14030 * used in the trigger's WHEN condition. The first case would
14031 * not require any extra work, but the second case would
14032 * require updating the WHEN expression, which will take a
14033 * significant amount of new code. Since we can't easily tell
14034 * which case applies, we punt for both. FIXME someday.
14036 if (subtype == AT_AlterColumnType)
14037 ereport(ERROR,
14038 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14039 errmsg("cannot alter type of a column used in a trigger definition"),
14040 errdetail("%s depends on column \"%s\"",
14041 getObjectDescription(&foundObject, false),
14042 colName)));
14043 break;
14045 case OCLASS_POLICY:
14048 * A policy can depend on a column because the column is
14049 * specified in the policy's USING or WITH CHECK qual
14050 * expressions. It might be possible to rewrite and recheck
14051 * the policy expression, but punt for now. It's certainly
14052 * easy enough to remove and recreate the policy; still, FIXME
14053 * someday.
14055 if (subtype == AT_AlterColumnType)
14056 ereport(ERROR,
14057 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14058 errmsg("cannot alter type of a column used in a policy definition"),
14059 errdetail("%s depends on column \"%s\"",
14060 getObjectDescription(&foundObject, false),
14061 colName)));
14062 break;
14064 case OCLASS_DEFAULT:
14066 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14068 if (col.objectId == RelationGetRelid(rel) &&
14069 col.objectSubId == attnum)
14072 * Ignore the column's own default expression. The
14073 * caller deals with it.
14076 else
14079 * This must be a reference from the expression of a
14080 * generated column elsewhere in the same table.
14081 * Changing the type/generated expression of a column
14082 * that is used by a generated column is not allowed
14083 * by SQL standard, so just punt for now. It might be
14084 * doable with some thinking and effort.
14086 if (subtype == AT_AlterColumnType)
14087 ereport(ERROR,
14088 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14089 errmsg("cannot alter type of a column used by a generated column"),
14090 errdetail("Column \"%s\" is used by generated column \"%s\".",
14091 colName,
14092 get_attname(col.objectId,
14093 col.objectSubId,
14094 false))));
14096 break;
14099 case OCLASS_STATISTIC_EXT:
14102 * Give the extended-stats machinery a chance to fix anything
14103 * that this column type change would break.
14105 RememberStatisticsForRebuilding(foundObject.objectId, tab);
14106 break;
14108 case OCLASS_PROC:
14109 case OCLASS_TYPE:
14110 case OCLASS_CAST:
14111 case OCLASS_COLLATION:
14112 case OCLASS_CONVERSION:
14113 case OCLASS_LANGUAGE:
14114 case OCLASS_LARGEOBJECT:
14115 case OCLASS_OPERATOR:
14116 case OCLASS_OPCLASS:
14117 case OCLASS_OPFAMILY:
14118 case OCLASS_AM:
14119 case OCLASS_AMOP:
14120 case OCLASS_AMPROC:
14121 case OCLASS_SCHEMA:
14122 case OCLASS_TSPARSER:
14123 case OCLASS_TSDICT:
14124 case OCLASS_TSTEMPLATE:
14125 case OCLASS_TSCONFIG:
14126 case OCLASS_ROLE:
14127 case OCLASS_ROLE_MEMBERSHIP:
14128 case OCLASS_DATABASE:
14129 case OCLASS_TBLSPACE:
14130 case OCLASS_FDW:
14131 case OCLASS_FOREIGN_SERVER:
14132 case OCLASS_USER_MAPPING:
14133 case OCLASS_DEFACL:
14134 case OCLASS_EXTENSION:
14135 case OCLASS_EVENT_TRIGGER:
14136 case OCLASS_PARAMETER_ACL:
14137 case OCLASS_PUBLICATION:
14138 case OCLASS_PUBLICATION_NAMESPACE:
14139 case OCLASS_PUBLICATION_REL:
14140 case OCLASS_SUBSCRIPTION:
14141 case OCLASS_TRANSFORM:
14144 * We don't expect any of these sorts of objects to depend on
14145 * a column.
14147 elog(ERROR, "unexpected object depending on column: %s",
14148 getObjectDescription(&foundObject, false));
14149 break;
14152 * There's intentionally no default: case here; we want the
14153 * compiler to warn if a new OCLASS hasn't been handled above.
14158 systable_endscan(scan);
14159 table_close(depRel, NoLock);
14163 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14164 * needs to be reset.
14166 static void
14167 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14169 if (!get_index_isreplident(indoid))
14170 return;
14172 if (tab->replicaIdentityIndex)
14173 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14175 tab->replicaIdentityIndex = get_rel_name(indoid);
14179 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14181 static void
14182 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14184 if (!get_index_isclustered(indoid))
14185 return;
14187 if (tab->clusterOnIndex)
14188 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14190 tab->clusterOnIndex = get_rel_name(indoid);
14194 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14195 * to be rebuilt (which we might already know).
14197 static void
14198 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14201 * This de-duplication check is critical for two independent reasons: we
14202 * mustn't try to recreate the same constraint twice, and if a constraint
14203 * depends on more than one column whose type is to be altered, we must
14204 * capture its definition string before applying any of the column type
14205 * changes. ruleutils.c will get confused if we ask again later.
14207 if (!list_member_oid(tab->changedConstraintOids, conoid))
14209 /* OK, capture the constraint's existing definition string */
14210 char *defstring = pg_get_constraintdef_command(conoid);
14211 Oid indoid;
14213 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14214 conoid);
14215 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14216 defstring);
14219 * For the index of a constraint, if any, remember if it is used for
14220 * the table's replica identity or if it is a clustered index, so that
14221 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14222 * those properties.
14224 indoid = get_constraint_index(conoid);
14225 if (OidIsValid(indoid))
14227 RememberReplicaIdentityForRebuilding(indoid, tab);
14228 RememberClusterOnForRebuilding(indoid, tab);
14234 * Subroutine for ATExecAlterColumnType: remember that an index needs
14235 * to be rebuilt (which we might already know).
14237 static void
14238 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14241 * This de-duplication check is critical for two independent reasons: we
14242 * mustn't try to recreate the same index twice, and if an index depends
14243 * on more than one column whose type is to be altered, we must capture
14244 * its definition string before applying any of the column type changes.
14245 * ruleutils.c will get confused if we ask again later.
14247 if (!list_member_oid(tab->changedIndexOids, indoid))
14250 * Before adding it as an index-to-rebuild, we'd better see if it
14251 * belongs to a constraint, and if so rebuild the constraint instead.
14252 * Typically this check fails, because constraint indexes normally
14253 * have only dependencies on their constraint. But it's possible for
14254 * such an index to also have direct dependencies on table columns,
14255 * for example with a partial exclusion constraint.
14257 Oid conoid = get_index_constraint(indoid);
14259 if (OidIsValid(conoid))
14261 RememberConstraintForRebuilding(conoid, tab);
14263 else
14265 /* OK, capture the index's existing definition string */
14266 char *defstring = pg_get_indexdef_string(indoid);
14268 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14269 indoid);
14270 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14271 defstring);
14274 * Remember if this index is used for the table's replica identity
14275 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14276 * can queue up commands necessary to restore those properties.
14278 RememberReplicaIdentityForRebuilding(indoid, tab);
14279 RememberClusterOnForRebuilding(indoid, tab);
14285 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14286 * needs to be rebuilt (which we might already know).
14288 static void
14289 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14292 * This de-duplication check is critical for two independent reasons: we
14293 * mustn't try to recreate the same statistics object twice, and if the
14294 * statistics object depends on more than one column whose type is to be
14295 * altered, we must capture its definition string before applying any of
14296 * the type changes. ruleutils.c will get confused if we ask again later.
14298 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14300 /* OK, capture the statistics object's existing definition string */
14301 char *defstring = pg_get_statisticsobjdef_string(stxoid);
14303 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14304 stxoid);
14305 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14306 defstring);
14311 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14312 * operations for a particular relation. We have to drop and recreate all the
14313 * indexes and constraints that depend on the altered columns. We do the
14314 * actual dropping here, but re-creation is managed by adding work queue
14315 * entries to do those steps later.
14317 static void
14318 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14320 ObjectAddress obj;
14321 ObjectAddresses *objects;
14322 ListCell *def_item;
14323 ListCell *oid_item;
14326 * Collect all the constraints and indexes to drop so we can process them
14327 * in a single call. That way we don't have to worry about dependencies
14328 * among them.
14330 objects = new_object_addresses();
14333 * Re-parse the index and constraint definitions, and attach them to the
14334 * appropriate work queue entries. We do this before dropping because in
14335 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14336 * lock on the table the constraint is attached to, and we need to get
14337 * that before reparsing/dropping.
14339 * We can't rely on the output of deparsing to tell us which relation to
14340 * operate on, because concurrent activity might have made the name
14341 * resolve differently. Instead, we've got to use the OID of the
14342 * constraint or index we're processing to figure out which relation to
14343 * operate on.
14345 forboth(oid_item, tab->changedConstraintOids,
14346 def_item, tab->changedConstraintDefs)
14348 Oid oldId = lfirst_oid(oid_item);
14349 HeapTuple tup;
14350 Form_pg_constraint con;
14351 Oid relid;
14352 Oid confrelid;
14353 char contype;
14354 bool conislocal;
14356 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14357 if (!HeapTupleIsValid(tup)) /* should not happen */
14358 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14359 con = (Form_pg_constraint) GETSTRUCT(tup);
14360 if (OidIsValid(con->conrelid))
14361 relid = con->conrelid;
14362 else
14364 /* must be a domain constraint */
14365 relid = get_typ_typrelid(getBaseType(con->contypid));
14366 if (!OidIsValid(relid))
14367 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14369 confrelid = con->confrelid;
14370 contype = con->contype;
14371 conislocal = con->conislocal;
14372 ReleaseSysCache(tup);
14374 ObjectAddressSet(obj, ConstraintRelationId, oldId);
14375 add_exact_object_address(&obj, objects);
14378 * If the constraint is inherited (only), we don't want to inject a
14379 * new definition here; it'll get recreated when
14380 * ATAddCheckNNConstraint recurses from adding the parent table's
14381 * constraint. But we had to carry the info this far so that we can
14382 * drop the constraint below.
14384 if (!conislocal)
14385 continue;
14388 * When rebuilding an FK constraint that references the table we're
14389 * modifying, we might not yet have any lock on the FK's table, so get
14390 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14391 * step, so there's no value in asking for anything weaker.
14393 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14394 LockRelationOid(relid, AccessExclusiveLock);
14396 ATPostAlterTypeParse(oldId, relid, confrelid,
14397 (char *) lfirst(def_item),
14398 wqueue, lockmode, tab->rewrite);
14400 forboth(oid_item, tab->changedIndexOids,
14401 def_item, tab->changedIndexDefs)
14403 Oid oldId = lfirst_oid(oid_item);
14404 Oid relid;
14406 relid = IndexGetRelation(oldId, false);
14407 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14408 (char *) lfirst(def_item),
14409 wqueue, lockmode, tab->rewrite);
14411 ObjectAddressSet(obj, RelationRelationId, oldId);
14412 add_exact_object_address(&obj, objects);
14415 /* add dependencies for new statistics */
14416 forboth(oid_item, tab->changedStatisticsOids,
14417 def_item, tab->changedStatisticsDefs)
14419 Oid oldId = lfirst_oid(oid_item);
14420 Oid relid;
14422 relid = StatisticsGetRelation(oldId, false);
14423 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14424 (char *) lfirst(def_item),
14425 wqueue, lockmode, tab->rewrite);
14427 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14428 add_exact_object_address(&obj, objects);
14432 * Queue up command to restore replica identity index marking
14434 if (tab->replicaIdentityIndex)
14436 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14437 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14439 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14440 subcmd->name = tab->replicaIdentityIndex;
14441 cmd->subtype = AT_ReplicaIdentity;
14442 cmd->def = (Node *) subcmd;
14444 /* do it after indexes and constraints */
14445 tab->subcmds[AT_PASS_OLD_CONSTR] =
14446 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14450 * Queue up command to restore marking of index used for cluster.
14452 if (tab->clusterOnIndex)
14454 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14456 cmd->subtype = AT_ClusterOn;
14457 cmd->name = tab->clusterOnIndex;
14459 /* do it after indexes and constraints */
14460 tab->subcmds[AT_PASS_OLD_CONSTR] =
14461 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14465 * It should be okay to use DROP_RESTRICT here, since nothing else should
14466 * be depending on these objects.
14468 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14470 free_object_addresses(objects);
14473 * The objects will get recreated during subsequent passes over the work
14474 * queue.
14479 * Parse the previously-saved definition string for a constraint, index or
14480 * statistics object against the newly-established column data type(s), and
14481 * queue up the resulting command parsetrees for execution.
14483 * This might fail if, for example, you have a WHERE clause that uses an
14484 * operator that's not available for the new column type.
14486 static void
14487 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14488 List **wqueue, LOCKMODE lockmode, bool rewrite)
14490 List *raw_parsetree_list;
14491 List *querytree_list;
14492 ListCell *list_item;
14493 Relation rel;
14496 * We expect that we will get only ALTER TABLE and CREATE INDEX
14497 * statements. Hence, there is no need to pass them through
14498 * parse_analyze_*() or the rewriter, but instead we need to pass them
14499 * through parse_utilcmd.c to make them ready for execution.
14501 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14502 querytree_list = NIL;
14503 foreach(list_item, raw_parsetree_list)
14505 RawStmt *rs = lfirst_node(RawStmt, list_item);
14506 Node *stmt = rs->stmt;
14508 if (IsA(stmt, IndexStmt))
14509 querytree_list = lappend(querytree_list,
14510 transformIndexStmt(oldRelId,
14511 (IndexStmt *) stmt,
14512 cmd));
14513 else if (IsA(stmt, AlterTableStmt))
14515 List *beforeStmts;
14516 List *afterStmts;
14518 stmt = (Node *) transformAlterTableStmt(oldRelId,
14519 (AlterTableStmt *) stmt,
14520 cmd,
14521 &beforeStmts,
14522 &afterStmts);
14523 querytree_list = list_concat(querytree_list, beforeStmts);
14524 querytree_list = lappend(querytree_list, stmt);
14525 querytree_list = list_concat(querytree_list, afterStmts);
14527 else if (IsA(stmt, CreateStatsStmt))
14528 querytree_list = lappend(querytree_list,
14529 transformStatsStmt(oldRelId,
14530 (CreateStatsStmt *) stmt,
14531 cmd));
14532 else
14533 querytree_list = lappend(querytree_list, stmt);
14536 /* Caller should already have acquired whatever lock we need. */
14537 rel = relation_open(oldRelId, NoLock);
14540 * Attach each generated command to the proper place in the work queue.
14541 * Note this could result in creation of entirely new work-queue entries.
14543 * Also note that we have to tweak the command subtypes, because it turns
14544 * out that re-creation of indexes and constraints has to act a bit
14545 * differently from initial creation.
14547 foreach(list_item, querytree_list)
14549 Node *stm = (Node *) lfirst(list_item);
14550 AlteredTableInfo *tab;
14552 tab = ATGetQueueEntry(wqueue, rel);
14554 if (IsA(stm, IndexStmt))
14556 IndexStmt *stmt = (IndexStmt *) stm;
14557 AlterTableCmd *newcmd;
14559 if (!rewrite)
14560 TryReuseIndex(oldId, stmt);
14561 stmt->reset_default_tblspc = true;
14562 /* keep the index's comment */
14563 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14565 newcmd = makeNode(AlterTableCmd);
14566 newcmd->subtype = AT_ReAddIndex;
14567 newcmd->def = (Node *) stmt;
14568 tab->subcmds[AT_PASS_OLD_INDEX] =
14569 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14571 else if (IsA(stm, AlterTableStmt))
14573 AlterTableStmt *stmt = (AlterTableStmt *) stm;
14574 ListCell *lcmd;
14576 foreach(lcmd, stmt->cmds)
14578 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14580 if (cmd->subtype == AT_AddIndex)
14582 IndexStmt *indstmt;
14583 Oid indoid;
14585 indstmt = castNode(IndexStmt, cmd->def);
14586 indoid = get_constraint_index(oldId);
14588 if (!rewrite)
14589 TryReuseIndex(indoid, indstmt);
14590 /* keep any comment on the index */
14591 indstmt->idxcomment = GetComment(indoid,
14592 RelationRelationId, 0);
14593 indstmt->reset_default_tblspc = true;
14595 cmd->subtype = AT_ReAddIndex;
14596 tab->subcmds[AT_PASS_OLD_INDEX] =
14597 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14599 /* recreate any comment on the constraint */
14600 RebuildConstraintComment(tab,
14601 AT_PASS_OLD_INDEX,
14602 oldId,
14603 rel,
14604 NIL,
14605 indstmt->idxname);
14607 else if (cmd->subtype == AT_AddConstraint)
14609 Constraint *con = castNode(Constraint, cmd->def);
14611 con->old_pktable_oid = refRelId;
14612 /* rewriting neither side of a FK */
14613 if (con->contype == CONSTR_FOREIGN &&
14614 !rewrite && tab->rewrite == 0)
14615 TryReuseForeignKey(oldId, con);
14616 con->reset_default_tblspc = true;
14617 cmd->subtype = AT_ReAddConstraint;
14618 tab->subcmds[AT_PASS_OLD_CONSTR] =
14619 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14621 /* recreate any comment on the constraint */
14622 RebuildConstraintComment(tab,
14623 AT_PASS_OLD_CONSTR,
14624 oldId,
14625 rel,
14626 NIL,
14627 con->conname);
14629 else if (cmd->subtype == AT_SetAttNotNull)
14632 * The parser will create AT_AttSetNotNull subcommands for
14633 * columns of PRIMARY KEY indexes/constraints, but we need
14634 * not do anything with them here, because the columns'
14635 * NOT NULL marks will already have been propagated into
14636 * the new table definition.
14639 else
14640 elog(ERROR, "unexpected statement subtype: %d",
14641 (int) cmd->subtype);
14644 else if (IsA(stm, AlterDomainStmt))
14646 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14648 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14650 Constraint *con = castNode(Constraint, stmt->def);
14651 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14653 cmd->subtype = AT_ReAddDomainConstraint;
14654 cmd->def = (Node *) stmt;
14655 tab->subcmds[AT_PASS_OLD_CONSTR] =
14656 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14658 /* recreate any comment on the constraint */
14659 RebuildConstraintComment(tab,
14660 AT_PASS_OLD_CONSTR,
14661 oldId,
14662 NULL,
14663 stmt->typeName,
14664 con->conname);
14666 else
14667 elog(ERROR, "unexpected statement subtype: %d",
14668 (int) stmt->subtype);
14670 else if (IsA(stm, CreateStatsStmt))
14672 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14673 AlterTableCmd *newcmd;
14675 /* keep the statistics object's comment */
14676 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14678 newcmd = makeNode(AlterTableCmd);
14679 newcmd->subtype = AT_ReAddStatistics;
14680 newcmd->def = (Node *) stmt;
14681 tab->subcmds[AT_PASS_MISC] =
14682 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14684 else
14685 elog(ERROR, "unexpected statement type: %d",
14686 (int) nodeTag(stm));
14689 relation_close(rel, NoLock);
14693 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14694 * for a table or domain constraint that is being rebuilt.
14696 * objid is the OID of the constraint.
14697 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14698 * as a string list) for a domain constraint.
14699 * (We could dig that info, as well as the conname, out of the pg_constraint
14700 * entry; but callers already have them so might as well pass them.)
14702 static void
14703 RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14704 Relation rel, List *domname,
14705 const char *conname)
14707 CommentStmt *cmd;
14708 char *comment_str;
14709 AlterTableCmd *newcmd;
14711 /* Look for comment for object wanted, and leave if none */
14712 comment_str = GetComment(objid, ConstraintRelationId, 0);
14713 if (comment_str == NULL)
14714 return;
14716 /* Build CommentStmt node, copying all input data for safety */
14717 cmd = makeNode(CommentStmt);
14718 if (rel)
14720 cmd->objtype = OBJECT_TABCONSTRAINT;
14721 cmd->object = (Node *)
14722 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14723 makeString(pstrdup(RelationGetRelationName(rel))),
14724 makeString(pstrdup(conname)));
14726 else
14728 cmd->objtype = OBJECT_DOMCONSTRAINT;
14729 cmd->object = (Node *)
14730 list_make2(makeTypeNameFromNameList(copyObject(domname)),
14731 makeString(pstrdup(conname)));
14733 cmd->comment = comment_str;
14735 /* Append it to list of commands */
14736 newcmd = makeNode(AlterTableCmd);
14737 newcmd->subtype = AT_ReAddComment;
14738 newcmd->def = (Node *) cmd;
14739 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14743 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14744 * for the real analysis, then mutates the IndexStmt based on that verdict.
14746 static void
14747 TryReuseIndex(Oid oldId, IndexStmt *stmt)
14749 if (CheckIndexCompatible(oldId,
14750 stmt->accessMethod,
14751 stmt->indexParams,
14752 stmt->excludeOpNames,
14753 stmt->iswithoutoverlaps))
14755 Relation irel = index_open(oldId, NoLock);
14757 /* If it's a partitioned index, there is no storage to share. */
14758 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14760 stmt->oldNumber = irel->rd_locator.relNumber;
14761 stmt->oldCreateSubid = irel->rd_createSubid;
14762 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14764 index_close(irel, NoLock);
14769 * Subroutine for ATPostAlterTypeParse().
14771 * Stash the old P-F equality operator into the Constraint node, for possible
14772 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14773 * this constraint can be skipped.
14775 static void
14776 TryReuseForeignKey(Oid oldId, Constraint *con)
14778 HeapTuple tup;
14779 Datum adatum;
14780 ArrayType *arr;
14781 Oid *rawarr;
14782 int numkeys;
14783 int i;
14785 Assert(con->contype == CONSTR_FOREIGN);
14786 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14788 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14789 if (!HeapTupleIsValid(tup)) /* should not happen */
14790 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14792 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14793 Anum_pg_constraint_conpfeqop);
14794 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14795 numkeys = ARR_DIMS(arr)[0];
14796 /* test follows the one in ri_FetchConstraintInfo() */
14797 if (ARR_NDIM(arr) != 1 ||
14798 ARR_HASNULL(arr) ||
14799 ARR_ELEMTYPE(arr) != OIDOID)
14800 elog(ERROR, "conpfeqop is not a 1-D Oid array");
14801 rawarr = (Oid *) ARR_DATA_PTR(arr);
14803 /* stash a List of the operator Oids in our Constraint node */
14804 for (i = 0; i < numkeys; i++)
14805 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14807 ReleaseSysCache(tup);
14811 * ALTER COLUMN .. OPTIONS ( ... )
14813 * Returns the address of the modified column
14815 static ObjectAddress
14816 ATExecAlterColumnGenericOptions(Relation rel,
14817 const char *colName,
14818 List *options,
14819 LOCKMODE lockmode)
14821 Relation ftrel;
14822 Relation attrel;
14823 ForeignServer *server;
14824 ForeignDataWrapper *fdw;
14825 HeapTuple tuple;
14826 HeapTuple newtuple;
14827 bool isnull;
14828 Datum repl_val[Natts_pg_attribute];
14829 bool repl_null[Natts_pg_attribute];
14830 bool repl_repl[Natts_pg_attribute];
14831 Datum datum;
14832 Form_pg_foreign_table fttableform;
14833 Form_pg_attribute atttableform;
14834 AttrNumber attnum;
14835 ObjectAddress address;
14837 if (options == NIL)
14838 return InvalidObjectAddress;
14840 /* First, determine FDW validator associated to the foreign table. */
14841 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14842 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14843 if (!HeapTupleIsValid(tuple))
14844 ereport(ERROR,
14845 (errcode(ERRCODE_UNDEFINED_OBJECT),
14846 errmsg("foreign table \"%s\" does not exist",
14847 RelationGetRelationName(rel))));
14848 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14849 server = GetForeignServer(fttableform->ftserver);
14850 fdw = GetForeignDataWrapper(server->fdwid);
14852 table_close(ftrel, AccessShareLock);
14853 ReleaseSysCache(tuple);
14855 attrel = table_open(AttributeRelationId, RowExclusiveLock);
14856 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14857 if (!HeapTupleIsValid(tuple))
14858 ereport(ERROR,
14859 (errcode(ERRCODE_UNDEFINED_COLUMN),
14860 errmsg("column \"%s\" of relation \"%s\" does not exist",
14861 colName, RelationGetRelationName(rel))));
14863 /* Prevent them from altering a system attribute */
14864 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14865 attnum = atttableform->attnum;
14866 if (attnum <= 0)
14867 ereport(ERROR,
14868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14869 errmsg("cannot alter system column \"%s\"", colName)));
14872 /* Initialize buffers for new tuple values */
14873 memset(repl_val, 0, sizeof(repl_val));
14874 memset(repl_null, false, sizeof(repl_null));
14875 memset(repl_repl, false, sizeof(repl_repl));
14877 /* Extract the current options */
14878 datum = SysCacheGetAttr(ATTNAME,
14879 tuple,
14880 Anum_pg_attribute_attfdwoptions,
14881 &isnull);
14882 if (isnull)
14883 datum = PointerGetDatum(NULL);
14885 /* Transform the options */
14886 datum = transformGenericOptions(AttributeRelationId,
14887 datum,
14888 options,
14889 fdw->fdwvalidator);
14891 if (PointerIsValid(DatumGetPointer(datum)))
14892 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14893 else
14894 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14896 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14898 /* Everything looks good - update the tuple */
14900 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14901 repl_val, repl_null, repl_repl);
14903 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14905 InvokeObjectPostAlterHook(RelationRelationId,
14906 RelationGetRelid(rel),
14907 atttableform->attnum);
14908 ObjectAddressSubSet(address, RelationRelationId,
14909 RelationGetRelid(rel), attnum);
14911 ReleaseSysCache(tuple);
14913 table_close(attrel, RowExclusiveLock);
14915 heap_freetuple(newtuple);
14917 return address;
14921 * ALTER TABLE OWNER
14923 * recursing is true if we are recursing from a table to its indexes,
14924 * sequences, or toast table. We don't allow the ownership of those things to
14925 * be changed separately from the parent table. Also, we can skip permission
14926 * checks (this is necessary not just an optimization, else we'd fail to
14927 * handle toast tables properly).
14929 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14930 * free-standing composite type.
14932 void
14933 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14935 Relation target_rel;
14936 Relation class_rel;
14937 HeapTuple tuple;
14938 Form_pg_class tuple_class;
14941 * Get exclusive lock till end of transaction on the target table. Use
14942 * relation_open so that we can work on indexes and sequences.
14944 target_rel = relation_open(relationOid, lockmode);
14946 /* Get its pg_class tuple, too */
14947 class_rel = table_open(RelationRelationId, RowExclusiveLock);
14949 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14950 if (!HeapTupleIsValid(tuple))
14951 elog(ERROR, "cache lookup failed for relation %u", relationOid);
14952 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14954 /* Can we change the ownership of this tuple? */
14955 switch (tuple_class->relkind)
14957 case RELKIND_RELATION:
14958 case RELKIND_VIEW:
14959 case RELKIND_MATVIEW:
14960 case RELKIND_FOREIGN_TABLE:
14961 case RELKIND_PARTITIONED_TABLE:
14962 /* ok to change owner */
14963 break;
14964 case RELKIND_INDEX:
14965 if (!recursing)
14968 * Because ALTER INDEX OWNER used to be allowed, and in fact
14969 * is generated by old versions of pg_dump, we give a warning
14970 * and do nothing rather than erroring out. Also, to avoid
14971 * unnecessary chatter while restoring those old dumps, say
14972 * nothing at all if the command would be a no-op anyway.
14974 if (tuple_class->relowner != newOwnerId)
14975 ereport(WARNING,
14976 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14977 errmsg("cannot change owner of index \"%s\"",
14978 NameStr(tuple_class->relname)),
14979 errhint("Change the ownership of the index's table instead.")));
14980 /* quick hack to exit via the no-op path */
14981 newOwnerId = tuple_class->relowner;
14983 break;
14984 case RELKIND_PARTITIONED_INDEX:
14985 if (recursing)
14986 break;
14987 ereport(ERROR,
14988 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14989 errmsg("cannot change owner of index \"%s\"",
14990 NameStr(tuple_class->relname)),
14991 errhint("Change the ownership of the index's table instead.")));
14992 break;
14993 case RELKIND_SEQUENCE:
14994 if (!recursing &&
14995 tuple_class->relowner != newOwnerId)
14997 /* if it's an owned sequence, disallow changing it by itself */
14998 Oid tableId;
14999 int32 colId;
15001 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15002 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15003 ereport(ERROR,
15004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15005 errmsg("cannot change owner of sequence \"%s\"",
15006 NameStr(tuple_class->relname)),
15007 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15008 NameStr(tuple_class->relname),
15009 get_rel_name(tableId))));
15011 break;
15012 case RELKIND_COMPOSITE_TYPE:
15013 if (recursing)
15014 break;
15015 ereport(ERROR,
15016 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15017 errmsg("\"%s\" is a composite type",
15018 NameStr(tuple_class->relname)),
15019 /* translator: %s is an SQL ALTER command */
15020 errhint("Use %s instead.",
15021 "ALTER TYPE")));
15022 break;
15023 case RELKIND_TOASTVALUE:
15024 if (recursing)
15025 break;
15026 /* FALL THRU */
15027 default:
15028 ereport(ERROR,
15029 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15030 errmsg("cannot change owner of relation \"%s\"",
15031 NameStr(tuple_class->relname)),
15032 errdetail_relkind_not_supported(tuple_class->relkind)));
15036 * If the new owner is the same as the existing owner, consider the
15037 * command to have succeeded. This is for dump restoration purposes.
15039 if (tuple_class->relowner != newOwnerId)
15041 Datum repl_val[Natts_pg_class];
15042 bool repl_null[Natts_pg_class];
15043 bool repl_repl[Natts_pg_class];
15044 Acl *newAcl;
15045 Datum aclDatum;
15046 bool isNull;
15047 HeapTuple newtuple;
15049 /* skip permission checks when recursing to index or toast table */
15050 if (!recursing)
15052 /* Superusers can always do it */
15053 if (!superuser())
15055 Oid namespaceOid = tuple_class->relnamespace;
15056 AclResult aclresult;
15058 /* Otherwise, must be owner of the existing object */
15059 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15060 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15061 RelationGetRelationName(target_rel));
15063 /* Must be able to become new owner */
15064 check_can_set_role(GetUserId(), newOwnerId);
15066 /* New owner must have CREATE privilege on namespace */
15067 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15068 ACL_CREATE);
15069 if (aclresult != ACLCHECK_OK)
15070 aclcheck_error(aclresult, OBJECT_SCHEMA,
15071 get_namespace_name(namespaceOid));
15075 memset(repl_null, false, sizeof(repl_null));
15076 memset(repl_repl, false, sizeof(repl_repl));
15078 repl_repl[Anum_pg_class_relowner - 1] = true;
15079 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15082 * Determine the modified ACL for the new owner. This is only
15083 * necessary when the ACL is non-null.
15085 aclDatum = SysCacheGetAttr(RELOID, tuple,
15086 Anum_pg_class_relacl,
15087 &isNull);
15088 if (!isNull)
15090 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15091 tuple_class->relowner, newOwnerId);
15092 repl_repl[Anum_pg_class_relacl - 1] = true;
15093 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15096 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15098 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15100 heap_freetuple(newtuple);
15103 * We must similarly update any per-column ACLs to reflect the new
15104 * owner; for neatness reasons that's split out as a subroutine.
15106 change_owner_fix_column_acls(relationOid,
15107 tuple_class->relowner,
15108 newOwnerId);
15111 * Update owner dependency reference, if any. A composite type has
15112 * none, because it's tracked for the pg_type entry instead of here;
15113 * indexes and TOAST tables don't have their own entries either.
15115 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15116 tuple_class->relkind != RELKIND_INDEX &&
15117 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15118 tuple_class->relkind != RELKIND_TOASTVALUE)
15119 changeDependencyOnOwner(RelationRelationId, relationOid,
15120 newOwnerId);
15123 * Also change the ownership of the table's row type, if it has one
15125 if (OidIsValid(tuple_class->reltype))
15126 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15129 * If we are operating on a table or materialized view, also change
15130 * the ownership of any indexes and sequences that belong to the
15131 * relation, as well as its toast table (if it has one).
15133 if (tuple_class->relkind == RELKIND_RELATION ||
15134 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15135 tuple_class->relkind == RELKIND_MATVIEW ||
15136 tuple_class->relkind == RELKIND_TOASTVALUE)
15138 List *index_oid_list;
15139 ListCell *i;
15141 /* Find all the indexes belonging to this relation */
15142 index_oid_list = RelationGetIndexList(target_rel);
15144 /* For each index, recursively change its ownership */
15145 foreach(i, index_oid_list)
15146 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15148 list_free(index_oid_list);
15151 /* If it has a toast table, recurse to change its ownership */
15152 if (tuple_class->reltoastrelid != InvalidOid)
15153 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15154 true, lockmode);
15156 /* If it has dependent sequences, recurse to change them too */
15157 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15160 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15162 ReleaseSysCache(tuple);
15163 table_close(class_rel, RowExclusiveLock);
15164 relation_close(target_rel, NoLock);
15168 * change_owner_fix_column_acls
15170 * Helper function for ATExecChangeOwner. Scan the columns of the table
15171 * and fix any non-null column ACLs to reflect the new owner.
15173 static void
15174 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15176 Relation attRelation;
15177 SysScanDesc scan;
15178 ScanKeyData key[1];
15179 HeapTuple attributeTuple;
15181 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15182 ScanKeyInit(&key[0],
15183 Anum_pg_attribute_attrelid,
15184 BTEqualStrategyNumber, F_OIDEQ,
15185 ObjectIdGetDatum(relationOid));
15186 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15187 true, NULL, 1, key);
15188 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15190 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15191 Datum repl_val[Natts_pg_attribute];
15192 bool repl_null[Natts_pg_attribute];
15193 bool repl_repl[Natts_pg_attribute];
15194 Acl *newAcl;
15195 Datum aclDatum;
15196 bool isNull;
15197 HeapTuple newtuple;
15199 /* Ignore dropped columns */
15200 if (att->attisdropped)
15201 continue;
15203 aclDatum = heap_getattr(attributeTuple,
15204 Anum_pg_attribute_attacl,
15205 RelationGetDescr(attRelation),
15206 &isNull);
15207 /* Null ACLs do not require changes */
15208 if (isNull)
15209 continue;
15211 memset(repl_null, false, sizeof(repl_null));
15212 memset(repl_repl, false, sizeof(repl_repl));
15214 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15215 oldOwnerId, newOwnerId);
15216 repl_repl[Anum_pg_attribute_attacl - 1] = true;
15217 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15219 newtuple = heap_modify_tuple(attributeTuple,
15220 RelationGetDescr(attRelation),
15221 repl_val, repl_null, repl_repl);
15223 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15225 heap_freetuple(newtuple);
15227 systable_endscan(scan);
15228 table_close(attRelation, RowExclusiveLock);
15232 * change_owner_recurse_to_sequences
15234 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15235 * for sequences that are dependent on serial columns, and changes their
15236 * ownership.
15238 static void
15239 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15241 Relation depRel;
15242 SysScanDesc scan;
15243 ScanKeyData key[2];
15244 HeapTuple tup;
15247 * SERIAL sequences are those having an auto dependency on one of the
15248 * table's columns (we don't care *which* column, exactly).
15250 depRel = table_open(DependRelationId, AccessShareLock);
15252 ScanKeyInit(&key[0],
15253 Anum_pg_depend_refclassid,
15254 BTEqualStrategyNumber, F_OIDEQ,
15255 ObjectIdGetDatum(RelationRelationId));
15256 ScanKeyInit(&key[1],
15257 Anum_pg_depend_refobjid,
15258 BTEqualStrategyNumber, F_OIDEQ,
15259 ObjectIdGetDatum(relationOid));
15260 /* we leave refobjsubid unspecified */
15262 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15263 NULL, 2, key);
15265 while (HeapTupleIsValid(tup = systable_getnext(scan)))
15267 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15268 Relation seqRel;
15270 /* skip dependencies other than auto dependencies on columns */
15271 if (depForm->refobjsubid == 0 ||
15272 depForm->classid != RelationRelationId ||
15273 depForm->objsubid != 0 ||
15274 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15275 continue;
15277 /* Use relation_open just in case it's an index */
15278 seqRel = relation_open(depForm->objid, lockmode);
15280 /* skip non-sequence relations */
15281 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15283 /* No need to keep the lock */
15284 relation_close(seqRel, lockmode);
15285 continue;
15288 /* We don't need to close the sequence while we alter it. */
15289 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15291 /* Now we can close it. Keep the lock till end of transaction. */
15292 relation_close(seqRel, NoLock);
15295 systable_endscan(scan);
15297 relation_close(depRel, AccessShareLock);
15301 * ALTER TABLE CLUSTER ON
15303 * The only thing we have to do is to change the indisclustered bits.
15305 * Return the address of the new clustering index.
15307 static ObjectAddress
15308 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15310 Oid indexOid;
15311 ObjectAddress address;
15313 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15315 if (!OidIsValid(indexOid))
15316 ereport(ERROR,
15317 (errcode(ERRCODE_UNDEFINED_OBJECT),
15318 errmsg("index \"%s\" for table \"%s\" does not exist",
15319 indexName, RelationGetRelationName(rel))));
15321 /* Check index is valid to cluster on */
15322 check_index_is_clusterable(rel, indexOid, lockmode);
15324 /* And do the work */
15325 mark_index_clustered(rel, indexOid, false);
15327 ObjectAddressSet(address,
15328 RelationRelationId, indexOid);
15330 return address;
15334 * ALTER TABLE SET WITHOUT CLUSTER
15336 * We have to find any indexes on the table that have indisclustered bit
15337 * set and turn it off.
15339 static void
15340 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15342 mark_index_clustered(rel, InvalidOid, false);
15346 * Preparation phase for SET ACCESS METHOD
15348 * Check that the access method exists and determine whether a change is
15349 * actually needed.
15351 static void
15352 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15354 Oid amoid;
15357 * Look up the access method name and check that it differs from the
15358 * table's current AM. If DEFAULT was specified for a partitioned table
15359 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15361 if (amname != NULL)
15362 amoid = get_table_am_oid(amname, false);
15363 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15364 amoid = InvalidOid;
15365 else
15366 amoid = get_table_am_oid(default_table_access_method, false);
15368 /* if it's a match, phase 3 doesn't need to do anything */
15369 if (rel->rd_rel->relam == amoid)
15370 return;
15372 /* Save info for Phase 3 to do the real work */
15373 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15374 tab->newAccessMethod = amoid;
15375 tab->chgAccessMethod = true;
15379 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15380 * storage that have an interest in preserving AM.
15382 * Since these have no storage, setting the access method is a catalog only
15383 * operation.
15385 static void
15386 ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15388 Relation pg_class;
15389 Oid oldAccessMethodId;
15390 HeapTuple tuple;
15391 Form_pg_class rd_rel;
15392 Oid reloid = RelationGetRelid(rel);
15395 * Shouldn't be called on relations having storage; these are processed in
15396 * phase 3.
15398 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15400 /* Get a modifiable copy of the relation's pg_class row. */
15401 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15403 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15404 if (!HeapTupleIsValid(tuple))
15405 elog(ERROR, "cache lookup failed for relation %u", reloid);
15406 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15408 /* Update the pg_class row. */
15409 oldAccessMethodId = rd_rel->relam;
15410 rd_rel->relam = newAccessMethodId;
15412 /* Leave if no update required */
15413 if (rd_rel->relam == oldAccessMethodId)
15415 heap_freetuple(tuple);
15416 table_close(pg_class, RowExclusiveLock);
15417 return;
15420 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15423 * Update the dependency on the new access method. No dependency is added
15424 * if the new access method is InvalidOid (default case). Be very careful
15425 * that this has to compare the previous value stored in pg_class with the
15426 * new one.
15428 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15430 ObjectAddress relobj,
15431 referenced;
15434 * New access method is defined and there was no dependency
15435 * previously, so record a new one.
15437 ObjectAddressSet(relobj, RelationRelationId, reloid);
15438 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15439 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15441 else if (OidIsValid(oldAccessMethodId) &&
15442 !OidIsValid(rd_rel->relam))
15445 * There was an access method defined, and no new one, so just remove
15446 * the existing dependency.
15448 deleteDependencyRecordsForClass(RelationRelationId, reloid,
15449 AccessMethodRelationId,
15450 DEPENDENCY_NORMAL);
15452 else
15454 Assert(OidIsValid(oldAccessMethodId) &&
15455 OidIsValid(rd_rel->relam));
15457 /* Both are valid, so update the dependency */
15458 changeDependencyFor(RelationRelationId, reloid,
15459 AccessMethodRelationId,
15460 oldAccessMethodId, rd_rel->relam);
15463 /* make the relam and dependency changes visible */
15464 CommandCounterIncrement();
15466 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15468 heap_freetuple(tuple);
15469 table_close(pg_class, RowExclusiveLock);
15473 * ALTER TABLE SET TABLESPACE
15475 static void
15476 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15478 Oid tablespaceId;
15480 /* Check that the tablespace exists */
15481 tablespaceId = get_tablespace_oid(tablespacename, false);
15483 /* Check permissions except when moving to database's default */
15484 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15486 AclResult aclresult;
15488 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15489 if (aclresult != ACLCHECK_OK)
15490 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15493 /* Save info for Phase 3 to do the real work */
15494 if (OidIsValid(tab->newTableSpace))
15495 ereport(ERROR,
15496 (errcode(ERRCODE_SYNTAX_ERROR),
15497 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15499 tab->newTableSpace = tablespaceId;
15503 * Set, reset, or replace reloptions.
15505 static void
15506 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15507 LOCKMODE lockmode)
15509 Oid relid;
15510 Relation pgclass;
15511 HeapTuple tuple;
15512 HeapTuple newtuple;
15513 Datum datum;
15514 bool isnull;
15515 Datum newOptions;
15516 Datum repl_val[Natts_pg_class];
15517 bool repl_null[Natts_pg_class];
15518 bool repl_repl[Natts_pg_class];
15519 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
15521 if (defList == NIL && operation != AT_ReplaceRelOptions)
15522 return; /* nothing to do */
15524 pgclass = table_open(RelationRelationId, RowExclusiveLock);
15526 /* Fetch heap tuple */
15527 relid = RelationGetRelid(rel);
15528 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15529 if (!HeapTupleIsValid(tuple))
15530 elog(ERROR, "cache lookup failed for relation %u", relid);
15532 if (operation == AT_ReplaceRelOptions)
15535 * If we're supposed to replace the reloptions list, we just pretend
15536 * there were none before.
15538 datum = (Datum) 0;
15539 isnull = true;
15541 else
15543 /* Get the old reloptions */
15544 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15545 &isnull);
15548 /* Generate new proposed reloptions (text array) */
15549 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15550 defList, NULL, validnsps, false,
15551 operation == AT_ResetRelOptions);
15553 /* Validate */
15554 switch (rel->rd_rel->relkind)
15556 case RELKIND_RELATION:
15557 case RELKIND_TOASTVALUE:
15558 case RELKIND_MATVIEW:
15559 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15560 break;
15561 case RELKIND_PARTITIONED_TABLE:
15562 (void) partitioned_table_reloptions(newOptions, true);
15563 break;
15564 case RELKIND_VIEW:
15565 (void) view_reloptions(newOptions, true);
15566 break;
15567 case RELKIND_INDEX:
15568 case RELKIND_PARTITIONED_INDEX:
15569 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15570 break;
15571 default:
15572 ereport(ERROR,
15573 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15574 errmsg("cannot set options for relation \"%s\"",
15575 RelationGetRelationName(rel)),
15576 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15577 break;
15580 /* Special-case validation of view options */
15581 if (rel->rd_rel->relkind == RELKIND_VIEW)
15583 Query *view_query = get_view_query(rel);
15584 List *view_options = untransformRelOptions(newOptions);
15585 ListCell *cell;
15586 bool check_option = false;
15588 foreach(cell, view_options)
15590 DefElem *defel = (DefElem *) lfirst(cell);
15592 if (strcmp(defel->defname, "check_option") == 0)
15593 check_option = true;
15597 * If the check option is specified, look to see if the view is
15598 * actually auto-updatable or not.
15600 if (check_option)
15602 const char *view_updatable_error =
15603 view_query_is_auto_updatable(view_query, true);
15605 if (view_updatable_error)
15606 ereport(ERROR,
15607 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15608 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15609 errhint("%s", _(view_updatable_error))));
15614 * All we need do here is update the pg_class row; the new options will be
15615 * propagated into relcaches during post-commit cache inval.
15617 memset(repl_val, 0, sizeof(repl_val));
15618 memset(repl_null, false, sizeof(repl_null));
15619 memset(repl_repl, false, sizeof(repl_repl));
15621 if (newOptions != (Datum) 0)
15622 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15623 else
15624 repl_null[Anum_pg_class_reloptions - 1] = true;
15626 repl_repl[Anum_pg_class_reloptions - 1] = true;
15628 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15629 repl_val, repl_null, repl_repl);
15631 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15633 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15635 heap_freetuple(newtuple);
15637 ReleaseSysCache(tuple);
15639 /* repeat the whole exercise for the toast table, if there's one */
15640 if (OidIsValid(rel->rd_rel->reltoastrelid))
15642 Relation toastrel;
15643 Oid toastid = rel->rd_rel->reltoastrelid;
15645 toastrel = table_open(toastid, lockmode);
15647 /* Fetch heap tuple */
15648 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15649 if (!HeapTupleIsValid(tuple))
15650 elog(ERROR, "cache lookup failed for relation %u", toastid);
15652 if (operation == AT_ReplaceRelOptions)
15655 * If we're supposed to replace the reloptions list, we just
15656 * pretend there were none before.
15658 datum = (Datum) 0;
15659 isnull = true;
15661 else
15663 /* Get the old reloptions */
15664 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15665 &isnull);
15668 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15669 defList, "toast", validnsps, false,
15670 operation == AT_ResetRelOptions);
15672 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15674 memset(repl_val, 0, sizeof(repl_val));
15675 memset(repl_null, false, sizeof(repl_null));
15676 memset(repl_repl, false, sizeof(repl_repl));
15678 if (newOptions != (Datum) 0)
15679 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15680 else
15681 repl_null[Anum_pg_class_reloptions - 1] = true;
15683 repl_repl[Anum_pg_class_reloptions - 1] = true;
15685 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15686 repl_val, repl_null, repl_repl);
15688 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15690 InvokeObjectPostAlterHookArg(RelationRelationId,
15691 RelationGetRelid(toastrel), 0,
15692 InvalidOid, true);
15694 heap_freetuple(newtuple);
15696 ReleaseSysCache(tuple);
15698 table_close(toastrel, NoLock);
15701 table_close(pgclass, RowExclusiveLock);
15705 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15706 * rewriting to be done, so we just want to copy the data as fast as possible.
15708 static void
15709 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15711 Relation rel;
15712 Oid reltoastrelid;
15713 RelFileNumber newrelfilenumber;
15714 RelFileLocator newrlocator;
15715 List *reltoastidxids = NIL;
15716 ListCell *lc;
15719 * Need lock here in case we are recursing to toast table or index
15721 rel = relation_open(tableOid, lockmode);
15723 /* Check first if relation can be moved to new tablespace */
15724 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15726 InvokeObjectPostAlterHook(RelationRelationId,
15727 RelationGetRelid(rel), 0);
15728 relation_close(rel, NoLock);
15729 return;
15732 reltoastrelid = rel->rd_rel->reltoastrelid;
15733 /* Fetch the list of indexes on toast relation if necessary */
15734 if (OidIsValid(reltoastrelid))
15736 Relation toastRel = relation_open(reltoastrelid, lockmode);
15738 reltoastidxids = RelationGetIndexList(toastRel);
15739 relation_close(toastRel, lockmode);
15743 * Relfilenumbers are not unique in databases across tablespaces, so we
15744 * need to allocate a new one in the new tablespace.
15746 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15747 rel->rd_rel->relpersistence);
15749 /* Open old and new relation */
15750 newrlocator = rel->rd_locator;
15751 newrlocator.relNumber = newrelfilenumber;
15752 newrlocator.spcOid = newTableSpace;
15754 /* hand off to AM to actually create new rel storage and copy the data */
15755 if (rel->rd_rel->relkind == RELKIND_INDEX)
15757 index_copy_data(rel, newrlocator);
15759 else
15761 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15762 table_relation_copy_data(rel, &newrlocator);
15766 * Update the pg_class row.
15768 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15769 * executed on pg_class or its indexes (the above copy wouldn't contain
15770 * the updated pg_class entry), but that's forbidden with
15771 * CheckRelationTableSpaceMove().
15773 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15775 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15777 RelationAssumeNewRelfilelocator(rel);
15779 relation_close(rel, NoLock);
15781 /* Make sure the reltablespace change is visible */
15782 CommandCounterIncrement();
15784 /* Move associated toast relation and/or indexes, too */
15785 if (OidIsValid(reltoastrelid))
15786 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15787 foreach(lc, reltoastidxids)
15788 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15790 /* Clean up */
15791 list_free(reltoastidxids);
15795 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15796 * storage that have an interest in preserving tablespace.
15798 * Since these have no storage the tablespace can be updated with a simple
15799 * metadata only operation to update the tablespace.
15801 static void
15802 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15805 * Shouldn't be called on relations having storage; these are processed in
15806 * phase 3.
15808 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15810 /* check if relation can be moved to its new tablespace */
15811 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15813 InvokeObjectPostAlterHook(RelationRelationId,
15814 RelationGetRelid(rel),
15816 return;
15819 /* Update can be done, so change reltablespace */
15820 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15822 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15824 /* Make sure the reltablespace change is visible */
15825 CommandCounterIncrement();
15829 * Alter Table ALL ... SET TABLESPACE
15831 * Allows a user to move all objects of some type in a given tablespace in the
15832 * current database to another tablespace. Objects can be chosen based on the
15833 * owner of the object also, to allow users to move only their objects.
15834 * The user must have CREATE rights on the new tablespace, as usual. The main
15835 * permissions handling is done by the lower-level table move function.
15837 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15838 * lock can't be acquired then we ereport(ERROR).
15841 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15843 List *relations = NIL;
15844 ListCell *l;
15845 ScanKeyData key[1];
15846 Relation rel;
15847 TableScanDesc scan;
15848 HeapTuple tuple;
15849 Oid orig_tablespaceoid;
15850 Oid new_tablespaceoid;
15851 List *role_oids = roleSpecsToIds(stmt->roles);
15853 /* Ensure we were not asked to move something we can't */
15854 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15855 stmt->objtype != OBJECT_MATVIEW)
15856 ereport(ERROR,
15857 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15858 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15860 /* Get the orig and new tablespace OIDs */
15861 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15862 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15864 /* Can't move shared relations in to or out of pg_global */
15865 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15866 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15867 new_tablespaceoid == GLOBALTABLESPACE_OID)
15868 ereport(ERROR,
15869 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15870 errmsg("cannot move relations in to or out of pg_global tablespace")));
15873 * Must have CREATE rights on the new tablespace, unless it is the
15874 * database default tablespace (which all users implicitly have CREATE
15875 * rights on).
15877 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15879 AclResult aclresult;
15881 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15882 ACL_CREATE);
15883 if (aclresult != ACLCHECK_OK)
15884 aclcheck_error(aclresult, OBJECT_TABLESPACE,
15885 get_tablespace_name(new_tablespaceoid));
15889 * Now that the checks are done, check if we should set either to
15890 * InvalidOid because it is our database's default tablespace.
15892 if (orig_tablespaceoid == MyDatabaseTableSpace)
15893 orig_tablespaceoid = InvalidOid;
15895 if (new_tablespaceoid == MyDatabaseTableSpace)
15896 new_tablespaceoid = InvalidOid;
15898 /* no-op */
15899 if (orig_tablespaceoid == new_tablespaceoid)
15900 return new_tablespaceoid;
15903 * Walk the list of objects in the tablespace and move them. This will
15904 * only find objects in our database, of course.
15906 ScanKeyInit(&key[0],
15907 Anum_pg_class_reltablespace,
15908 BTEqualStrategyNumber, F_OIDEQ,
15909 ObjectIdGetDatum(orig_tablespaceoid));
15911 rel = table_open(RelationRelationId, AccessShareLock);
15912 scan = table_beginscan_catalog(rel, 1, key);
15913 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15915 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15916 Oid relOid = relForm->oid;
15919 * Do not move objects in pg_catalog as part of this, if an admin
15920 * really wishes to do so, they can issue the individual ALTER
15921 * commands directly.
15923 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15924 * (TOAST will be moved with the main table).
15926 if (IsCatalogNamespace(relForm->relnamespace) ||
15927 relForm->relisshared ||
15928 isAnyTempNamespace(relForm->relnamespace) ||
15929 IsToastNamespace(relForm->relnamespace))
15930 continue;
15932 /* Only move the object type requested */
15933 if ((stmt->objtype == OBJECT_TABLE &&
15934 relForm->relkind != RELKIND_RELATION &&
15935 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15936 (stmt->objtype == OBJECT_INDEX &&
15937 relForm->relkind != RELKIND_INDEX &&
15938 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15939 (stmt->objtype == OBJECT_MATVIEW &&
15940 relForm->relkind != RELKIND_MATVIEW))
15941 continue;
15943 /* Check if we are only moving objects owned by certain roles */
15944 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15945 continue;
15948 * Handle permissions-checking here since we are locking the tables
15949 * and also to avoid doing a bunch of work only to fail part-way. Note
15950 * that permissions will also be checked by AlterTableInternal().
15952 * Caller must be considered an owner on the table to move it.
15954 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15955 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15956 NameStr(relForm->relname));
15958 if (stmt->nowait &&
15959 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15960 ereport(ERROR,
15961 (errcode(ERRCODE_OBJECT_IN_USE),
15962 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15963 get_namespace_name(relForm->relnamespace),
15964 NameStr(relForm->relname))));
15965 else
15966 LockRelationOid(relOid, AccessExclusiveLock);
15968 /* Add to our list of objects to move */
15969 relations = lappend_oid(relations, relOid);
15972 table_endscan(scan);
15973 table_close(rel, AccessShareLock);
15975 if (relations == NIL)
15976 ereport(NOTICE,
15977 (errcode(ERRCODE_NO_DATA_FOUND),
15978 errmsg("no matching relations in tablespace \"%s\" found",
15979 orig_tablespaceoid == InvalidOid ? "(database default)" :
15980 get_tablespace_name(orig_tablespaceoid))));
15982 /* Everything is locked, loop through and move all of the relations. */
15983 foreach(l, relations)
15985 List *cmds = NIL;
15986 AlterTableCmd *cmd = makeNode(AlterTableCmd);
15988 cmd->subtype = AT_SetTableSpace;
15989 cmd->name = stmt->new_tablespacename;
15991 cmds = lappend(cmds, cmd);
15993 EventTriggerAlterTableStart((Node *) stmt);
15994 /* OID is set by AlterTableInternal */
15995 AlterTableInternal(lfirst_oid(l), cmds, false);
15996 EventTriggerAlterTableEnd();
15999 return new_tablespaceoid;
16002 static void
16003 index_copy_data(Relation rel, RelFileLocator newrlocator)
16005 SMgrRelation dstrel;
16008 * Since we copy the file directly without looking at the shared buffers,
16009 * we'd better first flush out any pages of the source relation that are
16010 * in shared buffers. We assume no new changes will be made while we are
16011 * holding exclusive lock on the rel.
16013 FlushRelationBuffers(rel);
16016 * Create and copy all forks of the relation, and schedule unlinking of
16017 * old physical files.
16019 * NOTE: any conflict in relfilenumber value will be caught in
16020 * RelationCreateStorage().
16022 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16024 /* copy main fork */
16025 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16026 rel->rd_rel->relpersistence);
16028 /* copy those extra forks that exist */
16029 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16030 forkNum <= MAX_FORKNUM; forkNum++)
16032 if (smgrexists(RelationGetSmgr(rel), forkNum))
16034 smgrcreate(dstrel, forkNum, false);
16037 * WAL log creation if the relation is persistent, or this is the
16038 * init fork of an unlogged relation.
16040 if (RelationIsPermanent(rel) ||
16041 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16042 forkNum == INIT_FORKNUM))
16043 log_smgrcreate(&newrlocator, forkNum);
16044 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16045 rel->rd_rel->relpersistence);
16049 /* drop old relation, and close new one */
16050 RelationDropStorage(rel);
16051 smgrclose(dstrel);
16055 * ALTER TABLE ENABLE/DISABLE TRIGGER
16057 * We just pass this off to trigger.c.
16059 static void
16060 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16061 char fires_when, bool skip_system, bool recurse,
16062 LOCKMODE lockmode)
16064 EnableDisableTrigger(rel, trigname, InvalidOid,
16065 fires_when, skip_system, recurse,
16066 lockmode);
16068 InvokeObjectPostAlterHook(RelationRelationId,
16069 RelationGetRelid(rel), 0);
16073 * ALTER TABLE ENABLE/DISABLE RULE
16075 * We just pass this off to rewriteDefine.c.
16077 static void
16078 ATExecEnableDisableRule(Relation rel, const char *rulename,
16079 char fires_when, LOCKMODE lockmode)
16081 EnableDisableRule(rel, rulename, fires_when);
16083 InvokeObjectPostAlterHook(RelationRelationId,
16084 RelationGetRelid(rel), 0);
16088 * ALTER TABLE INHERIT
16090 * Add a parent to the child's parents. This verifies that all the columns and
16091 * check constraints of the parent appear in the child and that they have the
16092 * same data types and expressions.
16094 static void
16095 ATPrepAddInherit(Relation child_rel)
16097 if (child_rel->rd_rel->reloftype)
16098 ereport(ERROR,
16099 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16100 errmsg("cannot change inheritance of typed table")));
16102 if (child_rel->rd_rel->relispartition)
16103 ereport(ERROR,
16104 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16105 errmsg("cannot change inheritance of a partition")));
16107 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16108 ereport(ERROR,
16109 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16110 errmsg("cannot change inheritance of partitioned table")));
16114 * Return the address of the new parent relation.
16116 static ObjectAddress
16117 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16119 Relation parent_rel;
16120 List *children;
16121 ObjectAddress address;
16122 const char *trigger_name;
16125 * A self-exclusive lock is needed here. See the similar case in
16126 * MergeAttributes() for a full explanation.
16128 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16131 * Must be owner of both parent and child -- child was checked by
16132 * ATSimplePermissions call in ATPrepCmd
16134 ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
16136 /* Permanent rels cannot inherit from temporary ones */
16137 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16138 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16139 ereport(ERROR,
16140 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16141 errmsg("cannot inherit from temporary relation \"%s\"",
16142 RelationGetRelationName(parent_rel))));
16144 /* If parent rel is temp, it must belong to this session */
16145 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16146 !parent_rel->rd_islocaltemp)
16147 ereport(ERROR,
16148 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16149 errmsg("cannot inherit from temporary relation of another session")));
16151 /* Ditto for the child */
16152 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16153 !child_rel->rd_islocaltemp)
16154 ereport(ERROR,
16155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16156 errmsg("cannot inherit to temporary relation of another session")));
16158 /* Prevent partitioned tables from becoming inheritance parents */
16159 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16160 ereport(ERROR,
16161 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16162 errmsg("cannot inherit from partitioned table \"%s\"",
16163 parent->relname)));
16165 /* Likewise for partitions */
16166 if (parent_rel->rd_rel->relispartition)
16167 ereport(ERROR,
16168 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16169 errmsg("cannot inherit from a partition")));
16172 * Prevent circularity by seeing if proposed parent inherits from child.
16173 * (In particular, this disallows making a rel inherit from itself.)
16175 * This is not completely bulletproof because of race conditions: in
16176 * multi-level inheritance trees, someone else could concurrently be
16177 * making another inheritance link that closes the loop but does not join
16178 * either of the rels we have locked. Preventing that seems to require
16179 * exclusive locks on the entire inheritance tree, which is a cure worse
16180 * than the disease. find_all_inheritors() will cope with circularity
16181 * anyway, so don't sweat it too much.
16183 * We use weakest lock we can on child's children, namely AccessShareLock.
16185 children = find_all_inheritors(RelationGetRelid(child_rel),
16186 AccessShareLock, NULL);
16188 if (list_member_oid(children, RelationGetRelid(parent_rel)))
16189 ereport(ERROR,
16190 (errcode(ERRCODE_DUPLICATE_TABLE),
16191 errmsg("circular inheritance not allowed"),
16192 errdetail("\"%s\" is already a child of \"%s\".",
16193 parent->relname,
16194 RelationGetRelationName(child_rel))));
16197 * If child_rel has row-level triggers with transition tables, we
16198 * currently don't allow it to become an inheritance child. See also
16199 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16201 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16202 if (trigger_name != NULL)
16203 ereport(ERROR,
16204 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16205 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16206 trigger_name, RelationGetRelationName(child_rel)),
16207 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16209 /* OK to create inheritance */
16210 CreateInheritance(child_rel, parent_rel, false);
16213 * If parent_rel has a primary key, then child_rel has not-null
16214 * constraints that make these columns as non nullable. Make those
16215 * constraints as inherited.
16217 ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
16219 ObjectAddressSet(address, RelationRelationId,
16220 RelationGetRelid(parent_rel));
16222 /* keep our lock on the parent relation until commit */
16223 table_close(parent_rel, NoLock);
16225 return address;
16229 * CreateInheritance
16230 * Catalog manipulation portion of creating inheritance between a child
16231 * table and a parent table.
16233 * Common to ATExecAddInherit() and ATExecAttachPartition().
16235 static void
16236 CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16238 Relation catalogRelation;
16239 SysScanDesc scan;
16240 ScanKeyData key;
16241 HeapTuple inheritsTuple;
16242 int32 inhseqno;
16244 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16245 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16248 * Check for duplicates in the list of parents, and determine the highest
16249 * inhseqno already present; we'll use the next one for the new parent.
16250 * Also, if proposed child is a partition, it cannot already be
16251 * inheriting.
16253 * Note: we do not reject the case where the child already inherits from
16254 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16256 ScanKeyInit(&key,
16257 Anum_pg_inherits_inhrelid,
16258 BTEqualStrategyNumber, F_OIDEQ,
16259 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16260 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16261 true, NULL, 1, &key);
16263 /* inhseqno sequences start at 1 */
16264 inhseqno = 0;
16265 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16267 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16269 if (inh->inhparent == RelationGetRelid(parent_rel))
16270 ereport(ERROR,
16271 (errcode(ERRCODE_DUPLICATE_TABLE),
16272 errmsg("relation \"%s\" would be inherited from more than once",
16273 RelationGetRelationName(parent_rel))));
16275 if (inh->inhseqno > inhseqno)
16276 inhseqno = inh->inhseqno;
16278 systable_endscan(scan);
16280 /* Match up the columns and bump attinhcount as needed */
16281 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16283 /* Match up the constraints and bump coninhcount as needed */
16284 MergeConstraintsIntoExisting(child_rel, parent_rel);
16287 * OK, it looks valid. Make the catalog entries that show inheritance.
16289 StoreCatalogInheritance1(RelationGetRelid(child_rel),
16290 RelationGetRelid(parent_rel),
16291 inhseqno + 1,
16292 catalogRelation,
16293 parent_rel->rd_rel->relkind ==
16294 RELKIND_PARTITIONED_TABLE);
16296 /* Now we're done with pg_inherits */
16297 table_close(catalogRelation, RowExclusiveLock);
16301 * Obtain the source-text form of the constraint expression for a check
16302 * constraint, given its pg_constraint tuple
16304 static char *
16305 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16307 Form_pg_constraint con;
16308 bool isnull;
16309 Datum attr;
16310 Datum expr;
16312 con = (Form_pg_constraint) GETSTRUCT(contup);
16313 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16314 if (isnull)
16315 elog(ERROR, "null conbin for constraint %u", con->oid);
16317 expr = DirectFunctionCall2(pg_get_expr, attr,
16318 ObjectIdGetDatum(con->conrelid));
16319 return TextDatumGetCString(expr);
16323 * Determine whether two check constraints are functionally equivalent
16325 * The test we apply is to see whether they reverse-compile to the same
16326 * source string. This insulates us from issues like whether attributes
16327 * have the same physical column numbers in parent and child relations.
16329 static bool
16330 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16332 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16333 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16335 if (acon->condeferrable != bcon->condeferrable ||
16336 acon->condeferred != bcon->condeferred ||
16337 strcmp(decompile_conbin(a, tupleDesc),
16338 decompile_conbin(b, tupleDesc)) != 0)
16339 return false;
16340 else
16341 return true;
16345 * Check columns in child table match up with columns in parent, and increment
16346 * their attinhcount.
16348 * Called by CreateInheritance
16350 * Currently all parent columns must be found in child. Missing columns are an
16351 * error. One day we might consider creating new columns like CREATE TABLE
16352 * does. However, that is widely unpopular --- in the common use case of
16353 * partitioned tables it's a foot-gun.
16355 * The data type must match exactly. If the parent column is NOT NULL then
16356 * the child must be as well. Defaults are not compared, however.
16358 static void
16359 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16361 Relation attrrel;
16362 TupleDesc parent_desc;
16364 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16365 parent_desc = RelationGetDescr(parent_rel);
16367 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16369 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16370 char *parent_attname = NameStr(parent_att->attname);
16371 HeapTuple tuple;
16373 /* Ignore dropped columns in the parent. */
16374 if (parent_att->attisdropped)
16375 continue;
16377 /* Find same column in child (matching on column name). */
16378 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16379 if (HeapTupleIsValid(tuple))
16381 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16383 if (parent_att->atttypid != child_att->atttypid ||
16384 parent_att->atttypmod != child_att->atttypmod)
16385 ereport(ERROR,
16386 (errcode(ERRCODE_DATATYPE_MISMATCH),
16387 errmsg("child table \"%s\" has different type for column \"%s\"",
16388 RelationGetRelationName(child_rel), parent_attname)));
16390 if (parent_att->attcollation != child_att->attcollation)
16391 ereport(ERROR,
16392 (errcode(ERRCODE_COLLATION_MISMATCH),
16393 errmsg("child table \"%s\" has different collation for column \"%s\"",
16394 RelationGetRelationName(child_rel), parent_attname)));
16397 * If the parent has a not-null constraint that's not NO INHERIT,
16398 * make sure the child has one too.
16400 * Other constraints are checked elsewhere.
16402 if (parent_att->attnotnull && !child_att->attnotnull)
16404 HeapTuple contup;
16406 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16407 parent_att->attnum);
16408 if (HeapTupleIsValid(contup) &&
16409 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16410 ereport(ERROR,
16411 errcode(ERRCODE_DATATYPE_MISMATCH),
16412 errmsg("column \"%s\" in child table must be marked NOT NULL",
16413 parent_attname));
16417 * Child column must be generated if and only if parent column is.
16419 if (parent_att->attgenerated && !child_att->attgenerated)
16420 ereport(ERROR,
16421 (errcode(ERRCODE_DATATYPE_MISMATCH),
16422 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16423 if (child_att->attgenerated && !parent_att->attgenerated)
16424 ereport(ERROR,
16425 (errcode(ERRCODE_DATATYPE_MISMATCH),
16426 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16429 * Regular inheritance children are independent enough not to
16430 * inherit identity columns. But partitions are integral part of
16431 * a partitioned table and inherit identity column.
16433 if (ispartition)
16434 child_att->attidentity = parent_att->attidentity;
16437 * OK, bump the child column's inheritance count. (If we fail
16438 * later on, this change will just roll back.)
16440 child_att->attinhcount++;
16441 if (child_att->attinhcount < 0)
16442 ereport(ERROR,
16443 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16444 errmsg("too many inheritance parents"));
16447 * In case of partitions, we must enforce that value of attislocal
16448 * is same in all partitions. (Note: there are only inherited
16449 * attributes in partitions)
16451 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16453 Assert(child_att->attinhcount == 1);
16454 child_att->attislocal = false;
16457 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16458 heap_freetuple(tuple);
16460 else
16462 ereport(ERROR,
16463 (errcode(ERRCODE_DATATYPE_MISMATCH),
16464 errmsg("child table is missing column \"%s\"", parent_attname)));
16468 table_close(attrrel, RowExclusiveLock);
16472 * Check constraints in child table match up with constraints in parent,
16473 * and increment their coninhcount.
16475 * Constraints that are marked ONLY in the parent are ignored.
16477 * Called by CreateInheritance
16479 * Currently all constraints in parent must be present in the child. One day we
16480 * may consider adding new constraints like CREATE TABLE does.
16482 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16483 * constraints. As long as tables have more like 10 constraints it shouldn't be
16484 * a problem though. Even 100 constraints ought not be the end of the world.
16486 * XXX See MergeWithExistingConstraint too if you change this code.
16488 static void
16489 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16491 Relation constraintrel;
16492 SysScanDesc parent_scan;
16493 ScanKeyData parent_key;
16494 HeapTuple parent_tuple;
16495 Oid parent_relid = RelationGetRelid(parent_rel);
16497 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16499 /* Outer loop scans through the parent's constraint definitions */
16500 ScanKeyInit(&parent_key,
16501 Anum_pg_constraint_conrelid,
16502 BTEqualStrategyNumber, F_OIDEQ,
16503 ObjectIdGetDatum(parent_relid));
16504 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16505 true, NULL, 1, &parent_key);
16507 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16509 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16510 SysScanDesc child_scan;
16511 ScanKeyData child_key;
16512 HeapTuple child_tuple;
16513 bool found = false;
16515 if (parent_con->contype != CONSTRAINT_CHECK &&
16516 parent_con->contype != CONSTRAINT_NOTNULL)
16517 continue;
16519 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16520 if (parent_con->connoinherit)
16521 continue;
16523 /* Search for a child constraint matching this one */
16524 ScanKeyInit(&child_key,
16525 Anum_pg_constraint_conrelid,
16526 BTEqualStrategyNumber, F_OIDEQ,
16527 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16528 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16529 true, NULL, 1, &child_key);
16531 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16533 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16534 HeapTuple child_copy;
16536 if (child_con->contype != parent_con->contype)
16537 continue;
16540 * CHECK constraint are matched by name, NOT NULL ones by
16541 * attribute number
16543 if (child_con->contype == CONSTRAINT_CHECK)
16545 if (strcmp(NameStr(parent_con->conname),
16546 NameStr(child_con->conname)) != 0)
16547 continue;
16549 else if (child_con->contype == CONSTRAINT_NOTNULL)
16551 AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
16552 AttrNumber child_attno = extractNotNullColumn(child_tuple);
16554 if (strcmp(get_attname(parent_relid, parent_attno, false),
16555 get_attname(RelationGetRelid(child_rel), child_attno,
16556 false)) != 0)
16557 continue;
16560 if (child_con->contype == CONSTRAINT_CHECK &&
16561 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16562 ereport(ERROR,
16563 (errcode(ERRCODE_DATATYPE_MISMATCH),
16564 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16565 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16568 * If the CHECK child constraint is "no inherit" then cannot
16569 * merge.
16571 * This is not desirable for not-null constraints, mostly because
16572 * it breaks our pg_upgrade strategy, but it also makes sense on
16573 * its own: if a child has its own not-null constraint and then
16574 * acquires a parent with the same constraint, then we start to
16575 * enforce that constraint for all the descendants of that child
16576 * too, if any.
16578 if (child_con->contype == CONSTRAINT_CHECK &&
16579 child_con->connoinherit)
16580 ereport(ERROR,
16581 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16582 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16583 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16586 * If the child constraint is "not valid" then cannot merge with a
16587 * valid parent constraint
16589 if (parent_con->convalidated && !child_con->convalidated)
16590 ereport(ERROR,
16591 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16592 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16593 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16596 * OK, bump the child constraint's inheritance count. (If we fail
16597 * later on, this change will just roll back.)
16599 child_copy = heap_copytuple(child_tuple);
16600 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16601 child_con->coninhcount++;
16602 if (child_con->coninhcount < 0)
16603 ereport(ERROR,
16604 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16605 errmsg("too many inheritance parents"));
16606 if (child_con->contype == CONSTRAINT_NOTNULL &&
16607 child_con->connoinherit)
16608 child_con->connoinherit = false;
16611 * In case of partitions, an inherited constraint must be
16612 * inherited only once since it cannot have multiple parents and
16613 * it is never considered local.
16615 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16617 Assert(child_con->coninhcount == 1);
16618 child_con->conislocal = false;
16621 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16622 heap_freetuple(child_copy);
16624 found = true;
16625 break;
16628 systable_endscan(child_scan);
16630 if (!found)
16632 if (parent_con->contype == CONSTRAINT_NOTNULL)
16633 ereport(ERROR,
16634 errcode(ERRCODE_DATATYPE_MISMATCH),
16635 errmsg("column \"%s\" in child table must be marked NOT NULL",
16636 get_attname(parent_relid,
16637 extractNotNullColumn(parent_tuple),
16638 false)));
16640 ereport(ERROR,
16641 (errcode(ERRCODE_DATATYPE_MISMATCH),
16642 errmsg("child table is missing constraint \"%s\"",
16643 NameStr(parent_con->conname))));
16647 systable_endscan(parent_scan);
16648 table_close(constraintrel, RowExclusiveLock);
16652 * ALTER TABLE NO INHERIT
16654 * Return value is the address of the relation that is no longer parent.
16656 static ObjectAddress
16657 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16659 ObjectAddress address;
16660 Relation parent_rel;
16662 if (rel->rd_rel->relispartition)
16663 ereport(ERROR,
16664 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16665 errmsg("cannot change inheritance of a partition")));
16668 * AccessShareLock on the parent is probably enough, seeing that DROP
16669 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16670 * be inspecting the parent's schema.
16672 parent_rel = table_openrv(parent, AccessShareLock);
16675 * We don't bother to check ownership of the parent table --- ownership of
16676 * the child is presumed enough rights.
16679 /* Off to RemoveInheritance() where most of the work happens */
16680 RemoveInheritance(rel, parent_rel, false);
16683 * If parent_rel has a primary key, then child_rel has not-null
16684 * constraints that make these columns as non nullable. Mark those
16685 * constraints as no longer inherited by this parent.
16687 ATInheritAdjustNotNulls(parent_rel, rel, -1);
16690 * If the parent has a primary key, then we decrement counts for all NOT
16691 * NULL constraints
16694 ObjectAddressSet(address, RelationRelationId,
16695 RelationGetRelid(parent_rel));
16697 /* keep our lock on the parent relation until commit */
16698 table_close(parent_rel, NoLock);
16700 return address;
16704 * MarkInheritDetached
16706 * Set inhdetachpending for a partition, for ATExecDetachPartition
16707 * in concurrent mode. While at it, verify that no other partition is
16708 * already pending detach.
16710 static void
16711 MarkInheritDetached(Relation child_rel, Relation parent_rel)
16713 Relation catalogRelation;
16714 SysScanDesc scan;
16715 ScanKeyData key;
16716 HeapTuple inheritsTuple;
16717 bool found = false;
16719 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16722 * Find pg_inherits entries by inhparent. (We need to scan them all in
16723 * order to verify that no other partition is pending detach.)
16725 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16726 ScanKeyInit(&key,
16727 Anum_pg_inherits_inhparent,
16728 BTEqualStrategyNumber, F_OIDEQ,
16729 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16730 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16731 true, NULL, 1, &key);
16733 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16735 Form_pg_inherits inhForm;
16737 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16738 if (inhForm->inhdetachpending)
16739 ereport(ERROR,
16740 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16741 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16742 get_rel_name(inhForm->inhrelid),
16743 get_namespace_name(parent_rel->rd_rel->relnamespace),
16744 RelationGetRelationName(parent_rel)),
16745 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16747 if (inhForm->inhrelid == RelationGetRelid(child_rel))
16749 HeapTuple newtup;
16751 newtup = heap_copytuple(inheritsTuple);
16752 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16754 CatalogTupleUpdate(catalogRelation,
16755 &inheritsTuple->t_self,
16756 newtup);
16757 found = true;
16758 heap_freetuple(newtup);
16759 /* keep looking, to ensure we catch others pending detach */
16763 /* Done */
16764 systable_endscan(scan);
16765 table_close(catalogRelation, RowExclusiveLock);
16767 if (!found)
16768 ereport(ERROR,
16769 (errcode(ERRCODE_UNDEFINED_TABLE),
16770 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16771 RelationGetRelationName(child_rel),
16772 RelationGetRelationName(parent_rel))));
16776 * RemoveInheritance
16778 * Drop a parent from the child's parents. This just adjusts the attinhcount
16779 * and attislocal of the columns and removes the pg_inherit and pg_depend
16780 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16782 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16783 * up attislocal stays true, which means if a child is ever removed from a
16784 * parent then its columns will never be automatically dropped which may
16785 * surprise. But at least we'll never surprise by dropping columns someone
16786 * isn't expecting to be dropped which would actually mean data loss.
16788 * coninhcount and conislocal for inherited constraints are adjusted in
16789 * exactly the same way.
16791 * Common to ATExecDropInherit() and ATExecDetachPartition().
16793 static void
16794 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16796 Relation catalogRelation;
16797 SysScanDesc scan;
16798 ScanKeyData key[3];
16799 HeapTuple attributeTuple,
16800 constraintTuple;
16801 List *connames;
16802 List *nncolumns;
16803 bool found;
16804 bool is_partitioning;
16806 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16808 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16809 RelationGetRelid(parent_rel),
16810 expect_detached,
16811 RelationGetRelationName(child_rel));
16812 if (!found)
16814 if (is_partitioning)
16815 ereport(ERROR,
16816 (errcode(ERRCODE_UNDEFINED_TABLE),
16817 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16818 RelationGetRelationName(child_rel),
16819 RelationGetRelationName(parent_rel))));
16820 else
16821 ereport(ERROR,
16822 (errcode(ERRCODE_UNDEFINED_TABLE),
16823 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16824 RelationGetRelationName(parent_rel),
16825 RelationGetRelationName(child_rel))));
16829 * Search through child columns looking for ones matching parent rel
16831 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16832 ScanKeyInit(&key[0],
16833 Anum_pg_attribute_attrelid,
16834 BTEqualStrategyNumber, F_OIDEQ,
16835 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16836 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16837 true, NULL, 1, key);
16838 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16840 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16842 /* Ignore if dropped or not inherited */
16843 if (att->attisdropped)
16844 continue;
16845 if (att->attinhcount <= 0)
16846 continue;
16848 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16849 NameStr(att->attname)))
16851 /* Decrement inhcount and possibly set islocal to true */
16852 HeapTuple copyTuple = heap_copytuple(attributeTuple);
16853 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16855 copy_att->attinhcount--;
16856 if (copy_att->attinhcount == 0)
16857 copy_att->attislocal = true;
16859 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16860 heap_freetuple(copyTuple);
16863 systable_endscan(scan);
16864 table_close(catalogRelation, RowExclusiveLock);
16867 * Likewise, find inherited check constraints and disinherit them. To do
16868 * this, we first need a list of the names of the parent's check
16869 * constraints. (We cheat a bit by only checking for name matches,
16870 * assuming that the expressions will match.)
16872 * For NOT NULL columns, we store column numbers to match.
16874 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16875 ScanKeyInit(&key[0],
16876 Anum_pg_constraint_conrelid,
16877 BTEqualStrategyNumber, F_OIDEQ,
16878 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16879 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16880 true, NULL, 1, key);
16882 connames = NIL;
16883 nncolumns = NIL;
16885 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16887 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16889 if (con->contype == CONSTRAINT_CHECK)
16890 connames = lappend(connames, pstrdup(NameStr(con->conname)));
16891 if (con->contype == CONSTRAINT_NOTNULL)
16892 nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
16895 systable_endscan(scan);
16897 /* Now scan the child's constraints */
16898 ScanKeyInit(&key[0],
16899 Anum_pg_constraint_conrelid,
16900 BTEqualStrategyNumber, F_OIDEQ,
16901 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16902 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16903 true, NULL, 1, key);
16905 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16907 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16908 bool match = false;
16909 ListCell *lc;
16912 * Match CHECK constraints by name, not-null constraints by column
16913 * number, and ignore all others.
16915 if (con->contype == CONSTRAINT_CHECK)
16917 foreach(lc, connames)
16919 if (con->contype == CONSTRAINT_CHECK &&
16920 strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
16922 match = true;
16923 break;
16927 else if (con->contype == CONSTRAINT_NOTNULL)
16929 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16931 foreach(lc, nncolumns)
16933 if (lfirst_int(lc) == child_attno)
16935 match = true;
16936 break;
16940 else
16941 continue;
16943 if (match)
16945 /* Decrement inhcount and possibly set islocal to true */
16946 HeapTuple copyTuple = heap_copytuple(constraintTuple);
16947 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16949 if (copy_con->coninhcount <= 0) /* shouldn't happen */
16950 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16951 RelationGetRelid(child_rel), NameStr(copy_con->conname));
16953 copy_con->coninhcount--;
16954 if (copy_con->coninhcount == 0)
16955 copy_con->conislocal = true;
16957 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16958 heap_freetuple(copyTuple);
16962 systable_endscan(scan);
16963 table_close(catalogRelation, RowExclusiveLock);
16965 drop_parent_dependency(RelationGetRelid(child_rel),
16966 RelationRelationId,
16967 RelationGetRelid(parent_rel),
16968 child_dependency_type(is_partitioning));
16971 * Post alter hook of this inherits. Since object_access_hook doesn't take
16972 * multiple object identifiers, we relay oid of parent relation using
16973 * auxiliary_id argument.
16975 InvokeObjectPostAlterHookArg(InheritsRelationId,
16976 RelationGetRelid(child_rel), 0,
16977 RelationGetRelid(parent_rel), false);
16981 * Adjust coninhcount of not-null constraints upwards or downwards when a
16982 * table is marked as inheriting or no longer doing so a table with a primary
16983 * key.
16985 * Note: these constraints are not dropped, even if their inhcount goes to zero
16986 * and conislocal is false. Instead we mark the constraints as locally defined.
16987 * This is seen as more useful behavior, with no downsides. The user can always
16988 * drop them afterwards.
16990 static void
16991 ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
16993 Bitmapset *pkattnos;
16995 /* Quick exit when parent has no PK */
16996 if (!parent_rel->rd_rel->relhasindex)
16997 return;
16999 pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17000 INDEX_ATTR_BITMAP_PRIMARY_KEY);
17001 if (pkattnos != NULL)
17003 Bitmapset *childattnums = NULL;
17004 AttrMap *attmap;
17005 int i;
17007 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17008 RelationGetDescr(child_rel), true);
17010 i = -1;
17011 while ((i = bms_next_member(pkattnos, i)) >= 0)
17013 childattnums = bms_add_member(childattnums,
17014 attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
17018 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17019 * parent: the relevant not-null constraint in the child already had
17020 * its inhcount modified earlier.
17022 CommandCounterIncrement();
17023 AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17024 inhcount);
17029 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17030 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17031 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17032 * be TypeRelationId). There's no convenient way to do this, so go trawling
17033 * through pg_depend.
17035 static void
17036 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17037 DependencyType deptype)
17039 Relation catalogRelation;
17040 SysScanDesc scan;
17041 ScanKeyData key[3];
17042 HeapTuple depTuple;
17044 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17046 ScanKeyInit(&key[0],
17047 Anum_pg_depend_classid,
17048 BTEqualStrategyNumber, F_OIDEQ,
17049 ObjectIdGetDatum(RelationRelationId));
17050 ScanKeyInit(&key[1],
17051 Anum_pg_depend_objid,
17052 BTEqualStrategyNumber, F_OIDEQ,
17053 ObjectIdGetDatum(relid));
17054 ScanKeyInit(&key[2],
17055 Anum_pg_depend_objsubid,
17056 BTEqualStrategyNumber, F_INT4EQ,
17057 Int32GetDatum(0));
17059 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17060 NULL, 3, key);
17062 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17064 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17066 if (dep->refclassid == refclassid &&
17067 dep->refobjid == refobjid &&
17068 dep->refobjsubid == 0 &&
17069 dep->deptype == deptype)
17070 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17073 systable_endscan(scan);
17074 table_close(catalogRelation, RowExclusiveLock);
17078 * ALTER TABLE OF
17080 * Attach a table to a composite type, as though it had been created with CREATE
17081 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17082 * subject table must not have inheritance parents. These restrictions ensure
17083 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17085 * The address of the type is returned.
17087 static ObjectAddress
17088 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17090 Oid relid = RelationGetRelid(rel);
17091 Type typetuple;
17092 Form_pg_type typeform;
17093 Oid typeid;
17094 Relation inheritsRelation,
17095 relationRelation;
17096 SysScanDesc scan;
17097 ScanKeyData key;
17098 AttrNumber table_attno,
17099 type_attno;
17100 TupleDesc typeTupleDesc,
17101 tableTupleDesc;
17102 ObjectAddress tableobj,
17103 typeobj;
17104 HeapTuple classtuple;
17106 /* Validate the type. */
17107 typetuple = typenameType(NULL, ofTypename, NULL);
17108 check_of_type(typetuple);
17109 typeform = (Form_pg_type) GETSTRUCT(typetuple);
17110 typeid = typeform->oid;
17112 /* Fail if the table has any inheritance parents. */
17113 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17114 ScanKeyInit(&key,
17115 Anum_pg_inherits_inhrelid,
17116 BTEqualStrategyNumber, F_OIDEQ,
17117 ObjectIdGetDatum(relid));
17118 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17119 true, NULL, 1, &key);
17120 if (HeapTupleIsValid(systable_getnext(scan)))
17121 ereport(ERROR,
17122 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17123 errmsg("typed tables cannot inherit")));
17124 systable_endscan(scan);
17125 table_close(inheritsRelation, AccessShareLock);
17128 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17129 * require that the order also match. However, attnotnull need not match.
17131 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17132 tableTupleDesc = RelationGetDescr(rel);
17133 table_attno = 1;
17134 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17136 Form_pg_attribute type_attr,
17137 table_attr;
17138 const char *type_attname,
17139 *table_attname;
17141 /* Get the next non-dropped type attribute. */
17142 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17143 if (type_attr->attisdropped)
17144 continue;
17145 type_attname = NameStr(type_attr->attname);
17147 /* Get the next non-dropped table attribute. */
17150 if (table_attno > tableTupleDesc->natts)
17151 ereport(ERROR,
17152 (errcode(ERRCODE_DATATYPE_MISMATCH),
17153 errmsg("table is missing column \"%s\"",
17154 type_attname)));
17155 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17156 table_attno++;
17157 } while (table_attr->attisdropped);
17158 table_attname = NameStr(table_attr->attname);
17160 /* Compare name. */
17161 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17162 ereport(ERROR,
17163 (errcode(ERRCODE_DATATYPE_MISMATCH),
17164 errmsg("table has column \"%s\" where type requires \"%s\"",
17165 table_attname, type_attname)));
17167 /* Compare type. */
17168 if (table_attr->atttypid != type_attr->atttypid ||
17169 table_attr->atttypmod != type_attr->atttypmod ||
17170 table_attr->attcollation != type_attr->attcollation)
17171 ereport(ERROR,
17172 (errcode(ERRCODE_DATATYPE_MISMATCH),
17173 errmsg("table \"%s\" has different type for column \"%s\"",
17174 RelationGetRelationName(rel), type_attname)));
17176 ReleaseTupleDesc(typeTupleDesc);
17178 /* Any remaining columns at the end of the table had better be dropped. */
17179 for (; table_attno <= tableTupleDesc->natts; table_attno++)
17181 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17182 table_attno - 1);
17184 if (!table_attr->attisdropped)
17185 ereport(ERROR,
17186 (errcode(ERRCODE_DATATYPE_MISMATCH),
17187 errmsg("table has extra column \"%s\"",
17188 NameStr(table_attr->attname))));
17191 /* If the table was already typed, drop the existing dependency. */
17192 if (rel->rd_rel->reloftype)
17193 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17194 DEPENDENCY_NORMAL);
17196 /* Record a dependency on the new type. */
17197 tableobj.classId = RelationRelationId;
17198 tableobj.objectId = relid;
17199 tableobj.objectSubId = 0;
17200 typeobj.classId = TypeRelationId;
17201 typeobj.objectId = typeid;
17202 typeobj.objectSubId = 0;
17203 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17205 /* Update pg_class.reloftype */
17206 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17207 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17208 if (!HeapTupleIsValid(classtuple))
17209 elog(ERROR, "cache lookup failed for relation %u", relid);
17210 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17211 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17213 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17215 heap_freetuple(classtuple);
17216 table_close(relationRelation, RowExclusiveLock);
17218 ReleaseSysCache(typetuple);
17220 return typeobj;
17224 * ALTER TABLE NOT OF
17226 * Detach a typed table from its originating type. Just clear reloftype and
17227 * remove the dependency.
17229 static void
17230 ATExecDropOf(Relation rel, LOCKMODE lockmode)
17232 Oid relid = RelationGetRelid(rel);
17233 Relation relationRelation;
17234 HeapTuple tuple;
17236 if (!OidIsValid(rel->rd_rel->reloftype))
17237 ereport(ERROR,
17238 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17239 errmsg("\"%s\" is not a typed table",
17240 RelationGetRelationName(rel))));
17243 * We don't bother to check ownership of the type --- ownership of the
17244 * table is presumed enough rights. No lock required on the type, either.
17247 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17248 DEPENDENCY_NORMAL);
17250 /* Clear pg_class.reloftype */
17251 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17252 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17253 if (!HeapTupleIsValid(tuple))
17254 elog(ERROR, "cache lookup failed for relation %u", relid);
17255 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17256 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17258 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17260 heap_freetuple(tuple);
17261 table_close(relationRelation, RowExclusiveLock);
17265 * relation_mark_replica_identity: Update a table's replica identity
17267 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17268 * index. Otherwise, it must be InvalidOid.
17270 * Caller had better hold an exclusive lock on the relation, as the results
17271 * of running two of these concurrently wouldn't be pretty.
17273 static void
17274 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17275 bool is_internal)
17277 Relation pg_index;
17278 Relation pg_class;
17279 HeapTuple pg_class_tuple;
17280 HeapTuple pg_index_tuple;
17281 Form_pg_class pg_class_form;
17282 Form_pg_index pg_index_form;
17283 ListCell *index;
17286 * Check whether relreplident has changed, and update it if so.
17288 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17289 pg_class_tuple = SearchSysCacheCopy1(RELOID,
17290 ObjectIdGetDatum(RelationGetRelid(rel)));
17291 if (!HeapTupleIsValid(pg_class_tuple))
17292 elog(ERROR, "cache lookup failed for relation \"%s\"",
17293 RelationGetRelationName(rel));
17294 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17295 if (pg_class_form->relreplident != ri_type)
17297 pg_class_form->relreplident = ri_type;
17298 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17300 table_close(pg_class, RowExclusiveLock);
17301 heap_freetuple(pg_class_tuple);
17304 * Update the per-index indisreplident flags correctly.
17306 pg_index = table_open(IndexRelationId, RowExclusiveLock);
17307 foreach(index, RelationGetIndexList(rel))
17309 Oid thisIndexOid = lfirst_oid(index);
17310 bool dirty = false;
17312 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17313 ObjectIdGetDatum(thisIndexOid));
17314 if (!HeapTupleIsValid(pg_index_tuple))
17315 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17316 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17318 if (thisIndexOid == indexOid)
17320 /* Set the bit if not already set. */
17321 if (!pg_index_form->indisreplident)
17323 dirty = true;
17324 pg_index_form->indisreplident = true;
17327 else
17329 /* Unset the bit if set. */
17330 if (pg_index_form->indisreplident)
17332 dirty = true;
17333 pg_index_form->indisreplident = false;
17337 if (dirty)
17339 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17340 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17341 InvalidOid, is_internal);
17344 * Invalidate the relcache for the table, so that after we commit
17345 * all sessions will refresh the table's replica identity index
17346 * before attempting any UPDATE or DELETE on the table. (If we
17347 * changed the table's pg_class row above, then a relcache inval
17348 * is already queued due to that; but we might not have.)
17350 CacheInvalidateRelcache(rel);
17352 heap_freetuple(pg_index_tuple);
17355 table_close(pg_index, RowExclusiveLock);
17359 * ALTER TABLE <name> REPLICA IDENTITY ...
17361 static void
17362 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17364 Oid indexOid;
17365 Relation indexRel;
17366 int key;
17368 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17370 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17371 return;
17373 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17375 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17376 return;
17378 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17380 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17381 return;
17383 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17385 /* fallthrough */ ;
17387 else
17388 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17390 /* Check that the index exists */
17391 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17392 if (!OidIsValid(indexOid))
17393 ereport(ERROR,
17394 (errcode(ERRCODE_UNDEFINED_OBJECT),
17395 errmsg("index \"%s\" for table \"%s\" does not exist",
17396 stmt->name, RelationGetRelationName(rel))));
17398 indexRel = index_open(indexOid, ShareLock);
17400 /* Check that the index is on the relation we're altering. */
17401 if (indexRel->rd_index == NULL ||
17402 indexRel->rd_index->indrelid != RelationGetRelid(rel))
17403 ereport(ERROR,
17404 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17405 errmsg("\"%s\" is not an index for table \"%s\"",
17406 RelationGetRelationName(indexRel),
17407 RelationGetRelationName(rel))));
17408 /* The AM must support uniqueness, and the index must in fact be unique. */
17409 if (!indexRel->rd_indam->amcanunique ||
17410 !indexRel->rd_index->indisunique)
17411 ereport(ERROR,
17412 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17413 errmsg("cannot use non-unique index \"%s\" as replica identity",
17414 RelationGetRelationName(indexRel))));
17415 /* Deferred indexes are not guaranteed to be always unique. */
17416 if (!indexRel->rd_index->indimmediate)
17417 ereport(ERROR,
17418 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17419 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17420 RelationGetRelationName(indexRel))));
17421 /* Expression indexes aren't supported. */
17422 if (RelationGetIndexExpressions(indexRel) != NIL)
17423 ereport(ERROR,
17424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17425 errmsg("cannot use expression index \"%s\" as replica identity",
17426 RelationGetRelationName(indexRel))));
17427 /* Predicate indexes aren't supported. */
17428 if (RelationGetIndexPredicate(indexRel) != NIL)
17429 ereport(ERROR,
17430 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17431 errmsg("cannot use partial index \"%s\" as replica identity",
17432 RelationGetRelationName(indexRel))));
17434 /* Check index for nullable columns. */
17435 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17437 int16 attno = indexRel->rd_index->indkey.values[key];
17438 Form_pg_attribute attr;
17441 * Reject any other system columns. (Going forward, we'll disallow
17442 * indexes containing such columns in the first place, but they might
17443 * exist in older branches.)
17445 if (attno <= 0)
17446 ereport(ERROR,
17447 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17448 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17449 RelationGetRelationName(indexRel), attno)));
17451 attr = TupleDescAttr(rel->rd_att, attno - 1);
17452 if (!attr->attnotnull)
17453 ereport(ERROR,
17454 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17455 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17456 RelationGetRelationName(indexRel),
17457 NameStr(attr->attname))));
17460 /* This index is suitable for use as a replica identity. Mark it. */
17461 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17463 index_close(indexRel, NoLock);
17467 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17469 static void
17470 ATExecSetRowSecurity(Relation rel, bool rls)
17472 Relation pg_class;
17473 Oid relid;
17474 HeapTuple tuple;
17476 relid = RelationGetRelid(rel);
17478 /* Pull the record for this relation and update it */
17479 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17481 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17483 if (!HeapTupleIsValid(tuple))
17484 elog(ERROR, "cache lookup failed for relation %u", relid);
17486 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17487 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17489 InvokeObjectPostAlterHook(RelationRelationId,
17490 RelationGetRelid(rel), 0);
17492 table_close(pg_class, RowExclusiveLock);
17493 heap_freetuple(tuple);
17497 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17499 static void
17500 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17502 Relation pg_class;
17503 Oid relid;
17504 HeapTuple tuple;
17506 relid = RelationGetRelid(rel);
17508 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17510 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17512 if (!HeapTupleIsValid(tuple))
17513 elog(ERROR, "cache lookup failed for relation %u", relid);
17515 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17516 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17518 InvokeObjectPostAlterHook(RelationRelationId,
17519 RelationGetRelid(rel), 0);
17521 table_close(pg_class, RowExclusiveLock);
17522 heap_freetuple(tuple);
17526 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17528 static void
17529 ATExecGenericOptions(Relation rel, List *options)
17531 Relation ftrel;
17532 ForeignServer *server;
17533 ForeignDataWrapper *fdw;
17534 HeapTuple tuple;
17535 bool isnull;
17536 Datum repl_val[Natts_pg_foreign_table];
17537 bool repl_null[Natts_pg_foreign_table];
17538 bool repl_repl[Natts_pg_foreign_table];
17539 Datum datum;
17540 Form_pg_foreign_table tableform;
17542 if (options == NIL)
17543 return;
17545 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17547 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17548 ObjectIdGetDatum(rel->rd_id));
17549 if (!HeapTupleIsValid(tuple))
17550 ereport(ERROR,
17551 (errcode(ERRCODE_UNDEFINED_OBJECT),
17552 errmsg("foreign table \"%s\" does not exist",
17553 RelationGetRelationName(rel))));
17554 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17555 server = GetForeignServer(tableform->ftserver);
17556 fdw = GetForeignDataWrapper(server->fdwid);
17558 memset(repl_val, 0, sizeof(repl_val));
17559 memset(repl_null, false, sizeof(repl_null));
17560 memset(repl_repl, false, sizeof(repl_repl));
17562 /* Extract the current options */
17563 datum = SysCacheGetAttr(FOREIGNTABLEREL,
17564 tuple,
17565 Anum_pg_foreign_table_ftoptions,
17566 &isnull);
17567 if (isnull)
17568 datum = PointerGetDatum(NULL);
17570 /* Transform the options */
17571 datum = transformGenericOptions(ForeignTableRelationId,
17572 datum,
17573 options,
17574 fdw->fdwvalidator);
17576 if (PointerIsValid(DatumGetPointer(datum)))
17577 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17578 else
17579 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17581 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17583 /* Everything looks good - update the tuple */
17585 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17586 repl_val, repl_null, repl_repl);
17588 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17591 * Invalidate relcache so that all sessions will refresh any cached plans
17592 * that might depend on the old options.
17594 CacheInvalidateRelcache(rel);
17596 InvokeObjectPostAlterHook(ForeignTableRelationId,
17597 RelationGetRelid(rel), 0);
17599 table_close(ftrel, RowExclusiveLock);
17601 heap_freetuple(tuple);
17605 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17607 * Return value is the address of the modified column
17609 static ObjectAddress
17610 ATExecSetCompression(Relation rel,
17611 const char *column,
17612 Node *newValue,
17613 LOCKMODE lockmode)
17615 Relation attrel;
17616 HeapTuple tuple;
17617 Form_pg_attribute atttableform;
17618 AttrNumber attnum;
17619 char *compression;
17620 char cmethod;
17621 ObjectAddress address;
17623 compression = strVal(newValue);
17625 attrel = table_open(AttributeRelationId, RowExclusiveLock);
17627 /* copy the cache entry so we can scribble on it below */
17628 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17629 if (!HeapTupleIsValid(tuple))
17630 ereport(ERROR,
17631 (errcode(ERRCODE_UNDEFINED_COLUMN),
17632 errmsg("column \"%s\" of relation \"%s\" does not exist",
17633 column, RelationGetRelationName(rel))));
17635 /* prevent them from altering a system attribute */
17636 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17637 attnum = atttableform->attnum;
17638 if (attnum <= 0)
17639 ereport(ERROR,
17640 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17641 errmsg("cannot alter system column \"%s\"", column)));
17644 * Check that column type is compressible, then get the attribute
17645 * compression method code
17647 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17649 /* update pg_attribute entry */
17650 atttableform->attcompression = cmethod;
17651 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17653 InvokeObjectPostAlterHook(RelationRelationId,
17654 RelationGetRelid(rel),
17655 attnum);
17658 * Apply the change to indexes as well (only for simple index columns,
17659 * matching behavior of index.c ConstructTupleDescriptor()).
17661 SetIndexStorageProperties(rel, attrel, attnum,
17662 false, 0,
17663 true, cmethod,
17664 lockmode);
17666 heap_freetuple(tuple);
17668 table_close(attrel, RowExclusiveLock);
17670 /* make changes visible */
17671 CommandCounterIncrement();
17673 ObjectAddressSubSet(address, RelationRelationId,
17674 RelationGetRelid(rel), attnum);
17675 return address;
17680 * Preparation phase for SET LOGGED/UNLOGGED
17682 * This verifies that we're not trying to change a temp table. Also,
17683 * existing foreign key constraints are checked to avoid ending up with
17684 * permanent tables referencing unlogged tables.
17686 * Return value is false if the operation is a no-op (in which case the
17687 * checks are skipped), otherwise true.
17689 static bool
17690 ATPrepChangePersistence(Relation rel, bool toLogged)
17692 Relation pg_constraint;
17693 HeapTuple tuple;
17694 SysScanDesc scan;
17695 ScanKeyData skey[1];
17698 * Disallow changing status for a temp table. Also verify whether we can
17699 * get away with doing nothing; in such cases we don't need to run the
17700 * checks below, either.
17702 switch (rel->rd_rel->relpersistence)
17704 case RELPERSISTENCE_TEMP:
17705 ereport(ERROR,
17706 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17707 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17708 RelationGetRelationName(rel)),
17709 errtable(rel)));
17710 break;
17711 case RELPERSISTENCE_PERMANENT:
17712 if (toLogged)
17713 /* nothing to do */
17714 return false;
17715 break;
17716 case RELPERSISTENCE_UNLOGGED:
17717 if (!toLogged)
17718 /* nothing to do */
17719 return false;
17720 break;
17724 * Check that the table is not part of any publication when changing to
17725 * UNLOGGED, as UNLOGGED tables can't be published.
17727 if (!toLogged &&
17728 GetRelationPublications(RelationGetRelid(rel)) != NIL)
17729 ereport(ERROR,
17730 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17731 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17732 RelationGetRelationName(rel)),
17733 errdetail("Unlogged relations cannot be replicated.")));
17736 * Check existing foreign key constraints to preserve the invariant that
17737 * permanent tables cannot reference unlogged ones. Self-referencing
17738 * foreign keys can safely be ignored.
17740 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17743 * Scan conrelid if changing to permanent, else confrelid. This also
17744 * determines whether a useful index exists.
17746 ScanKeyInit(&skey[0],
17747 toLogged ? Anum_pg_constraint_conrelid :
17748 Anum_pg_constraint_confrelid,
17749 BTEqualStrategyNumber, F_OIDEQ,
17750 ObjectIdGetDatum(RelationGetRelid(rel)));
17751 scan = systable_beginscan(pg_constraint,
17752 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17753 true, NULL, 1, skey);
17755 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17757 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17759 if (con->contype == CONSTRAINT_FOREIGN)
17761 Oid foreignrelid;
17762 Relation foreignrel;
17764 /* the opposite end of what we used as scankey */
17765 foreignrelid = toLogged ? con->confrelid : con->conrelid;
17767 /* ignore if self-referencing */
17768 if (RelationGetRelid(rel) == foreignrelid)
17769 continue;
17771 foreignrel = relation_open(foreignrelid, AccessShareLock);
17773 if (toLogged)
17775 if (!RelationIsPermanent(foreignrel))
17776 ereport(ERROR,
17777 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17778 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17779 RelationGetRelationName(rel),
17780 RelationGetRelationName(foreignrel)),
17781 errtableconstraint(rel, NameStr(con->conname))));
17783 else
17785 if (RelationIsPermanent(foreignrel))
17786 ereport(ERROR,
17787 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17788 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17789 RelationGetRelationName(rel),
17790 RelationGetRelationName(foreignrel)),
17791 errtableconstraint(rel, NameStr(con->conname))));
17794 relation_close(foreignrel, AccessShareLock);
17798 systable_endscan(scan);
17800 table_close(pg_constraint, AccessShareLock);
17802 return true;
17806 * Execute ALTER TABLE SET SCHEMA
17808 ObjectAddress
17809 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17811 Relation rel;
17812 Oid relid;
17813 Oid oldNspOid;
17814 Oid nspOid;
17815 RangeVar *newrv;
17816 ObjectAddresses *objsMoved;
17817 ObjectAddress myself;
17819 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17820 stmt->missing_ok ? RVR_MISSING_OK : 0,
17821 RangeVarCallbackForAlterRelation,
17822 (void *) stmt);
17824 if (!OidIsValid(relid))
17826 ereport(NOTICE,
17827 (errmsg("relation \"%s\" does not exist, skipping",
17828 stmt->relation->relname)));
17829 return InvalidObjectAddress;
17832 rel = relation_open(relid, NoLock);
17834 oldNspOid = RelationGetNamespace(rel);
17836 /* If it's an owned sequence, disallow moving it by itself. */
17837 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17839 Oid tableId;
17840 int32 colId;
17842 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17843 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17844 ereport(ERROR,
17845 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17846 errmsg("cannot move an owned sequence into another schema"),
17847 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17848 RelationGetRelationName(rel),
17849 get_rel_name(tableId))));
17852 /* Get and lock schema OID and check its permissions. */
17853 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17854 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17856 /* common checks on switching namespaces */
17857 CheckSetNamespace(oldNspOid, nspOid);
17859 objsMoved = new_object_addresses();
17860 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17861 free_object_addresses(objsMoved);
17863 ObjectAddressSet(myself, RelationRelationId, relid);
17865 if (oldschema)
17866 *oldschema = oldNspOid;
17868 /* close rel, but keep lock until commit */
17869 relation_close(rel, NoLock);
17871 return myself;
17875 * The guts of relocating a table or materialized view to another namespace:
17876 * besides moving the relation itself, its dependent objects are relocated to
17877 * the new schema.
17879 void
17880 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17881 ObjectAddresses *objsMoved)
17883 Relation classRel;
17885 Assert(objsMoved != NULL);
17887 /* OK, modify the pg_class row and pg_depend entry */
17888 classRel = table_open(RelationRelationId, RowExclusiveLock);
17890 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17891 nspOid, true, objsMoved);
17893 /* Fix the table's row type too, if it has one */
17894 if (OidIsValid(rel->rd_rel->reltype))
17895 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17896 nspOid, false, false, objsMoved);
17898 /* Fix other dependent stuff */
17899 if (rel->rd_rel->relkind == RELKIND_RELATION ||
17900 rel->rd_rel->relkind == RELKIND_MATVIEW ||
17901 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17903 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17904 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17905 objsMoved, AccessExclusiveLock);
17906 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17907 false, objsMoved);
17910 table_close(classRel, RowExclusiveLock);
17914 * The guts of relocating a relation to another namespace: fix the pg_class
17915 * entry, and the pg_depend entry if any. Caller must already have
17916 * opened and write-locked pg_class.
17918 void
17919 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17920 Oid oldNspOid, Oid newNspOid,
17921 bool hasDependEntry,
17922 ObjectAddresses *objsMoved)
17924 HeapTuple classTup;
17925 Form_pg_class classForm;
17926 ObjectAddress thisobj;
17927 bool already_done = false;
17929 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17930 if (!HeapTupleIsValid(classTup))
17931 elog(ERROR, "cache lookup failed for relation %u", relOid);
17932 classForm = (Form_pg_class) GETSTRUCT(classTup);
17934 Assert(classForm->relnamespace == oldNspOid);
17936 thisobj.classId = RelationRelationId;
17937 thisobj.objectId = relOid;
17938 thisobj.objectSubId = 0;
17941 * If the object has already been moved, don't move it again. If it's
17942 * already in the right place, don't move it, but still fire the object
17943 * access hook.
17945 already_done = object_address_present(&thisobj, objsMoved);
17946 if (!already_done && oldNspOid != newNspOid)
17948 /* check for duplicate name (more friendly than unique-index failure) */
17949 if (get_relname_relid(NameStr(classForm->relname),
17950 newNspOid) != InvalidOid)
17951 ereport(ERROR,
17952 (errcode(ERRCODE_DUPLICATE_TABLE),
17953 errmsg("relation \"%s\" already exists in schema \"%s\"",
17954 NameStr(classForm->relname),
17955 get_namespace_name(newNspOid))));
17957 /* classTup is a copy, so OK to scribble on */
17958 classForm->relnamespace = newNspOid;
17960 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
17962 /* Update dependency on schema if caller said so */
17963 if (hasDependEntry &&
17964 changeDependencyFor(RelationRelationId,
17965 relOid,
17966 NamespaceRelationId,
17967 oldNspOid,
17968 newNspOid) != 1)
17969 elog(ERROR, "could not change schema dependency for relation \"%s\"",
17970 NameStr(classForm->relname));
17972 if (!already_done)
17974 add_exact_object_address(&thisobj, objsMoved);
17976 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17979 heap_freetuple(classTup);
17983 * Move all indexes for the specified relation to another namespace.
17985 * Note: we assume adequate permission checking was done by the caller,
17986 * and that the caller has a suitable lock on the owning relation.
17988 static void
17989 AlterIndexNamespaces(Relation classRel, Relation rel,
17990 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17992 List *indexList;
17993 ListCell *l;
17995 indexList = RelationGetIndexList(rel);
17997 foreach(l, indexList)
17999 Oid indexOid = lfirst_oid(l);
18000 ObjectAddress thisobj;
18002 thisobj.classId = RelationRelationId;
18003 thisobj.objectId = indexOid;
18004 thisobj.objectSubId = 0;
18007 * Note: currently, the index will not have its own dependency on the
18008 * namespace, so we don't need to do changeDependencyFor(). There's no
18009 * row type in pg_type, either.
18011 * XXX this objsMoved test may be pointless -- surely we have a single
18012 * dependency link from a relation to each index?
18014 if (!object_address_present(&thisobj, objsMoved))
18016 AlterRelationNamespaceInternal(classRel, indexOid,
18017 oldNspOid, newNspOid,
18018 false, objsMoved);
18019 add_exact_object_address(&thisobj, objsMoved);
18023 list_free(indexList);
18027 * Move all identity and SERIAL-column sequences of the specified relation to another
18028 * namespace.
18030 * Note: we assume adequate permission checking was done by the caller,
18031 * and that the caller has a suitable lock on the owning relation.
18033 static void
18034 AlterSeqNamespaces(Relation classRel, Relation rel,
18035 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18036 LOCKMODE lockmode)
18038 Relation depRel;
18039 SysScanDesc scan;
18040 ScanKeyData key[2];
18041 HeapTuple tup;
18044 * SERIAL sequences are those having an auto dependency on one of the
18045 * table's columns (we don't care *which* column, exactly).
18047 depRel = table_open(DependRelationId, AccessShareLock);
18049 ScanKeyInit(&key[0],
18050 Anum_pg_depend_refclassid,
18051 BTEqualStrategyNumber, F_OIDEQ,
18052 ObjectIdGetDatum(RelationRelationId));
18053 ScanKeyInit(&key[1],
18054 Anum_pg_depend_refobjid,
18055 BTEqualStrategyNumber, F_OIDEQ,
18056 ObjectIdGetDatum(RelationGetRelid(rel)));
18057 /* we leave refobjsubid unspecified */
18059 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18060 NULL, 2, key);
18062 while (HeapTupleIsValid(tup = systable_getnext(scan)))
18064 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18065 Relation seqRel;
18067 /* skip dependencies other than auto dependencies on columns */
18068 if (depForm->refobjsubid == 0 ||
18069 depForm->classid != RelationRelationId ||
18070 depForm->objsubid != 0 ||
18071 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18072 continue;
18074 /* Use relation_open just in case it's an index */
18075 seqRel = relation_open(depForm->objid, lockmode);
18077 /* skip non-sequence relations */
18078 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18080 /* No need to keep the lock */
18081 relation_close(seqRel, lockmode);
18082 continue;
18085 /* Fix the pg_class and pg_depend entries */
18086 AlterRelationNamespaceInternal(classRel, depForm->objid,
18087 oldNspOid, newNspOid,
18088 true, objsMoved);
18091 * Sequences used to have entries in pg_type, but no longer do. If we
18092 * ever re-instate that, we'll need to move the pg_type entry to the
18093 * new namespace, too (using AlterTypeNamespaceInternal).
18095 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18097 /* Now we can close it. Keep the lock till end of transaction. */
18098 relation_close(seqRel, NoLock);
18101 systable_endscan(scan);
18103 relation_close(depRel, AccessShareLock);
18108 * This code supports
18109 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18111 * Because we only support this for TEMP tables, it's sufficient to remember
18112 * the state in a backend-local data structure.
18116 * Register a newly-created relation's ON COMMIT action.
18118 void
18119 register_on_commit_action(Oid relid, OnCommitAction action)
18121 OnCommitItem *oc;
18122 MemoryContext oldcxt;
18125 * We needn't bother registering the relation unless there is an ON COMMIT
18126 * action we need to take.
18128 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18129 return;
18131 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18133 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18134 oc->relid = relid;
18135 oc->oncommit = action;
18136 oc->creating_subid = GetCurrentSubTransactionId();
18137 oc->deleting_subid = InvalidSubTransactionId;
18140 * We use lcons() here so that ON COMMIT actions are processed in reverse
18141 * order of registration. That might not be essential but it seems
18142 * reasonable.
18144 on_commits = lcons(oc, on_commits);
18146 MemoryContextSwitchTo(oldcxt);
18150 * Unregister any ON COMMIT action when a relation is deleted.
18152 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18154 void
18155 remove_on_commit_action(Oid relid)
18157 ListCell *l;
18159 foreach(l, on_commits)
18161 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18163 if (oc->relid == relid)
18165 oc->deleting_subid = GetCurrentSubTransactionId();
18166 break;
18172 * Perform ON COMMIT actions.
18174 * This is invoked just before actually committing, since it's possible
18175 * to encounter errors.
18177 void
18178 PreCommit_on_commit_actions(void)
18180 ListCell *l;
18181 List *oids_to_truncate = NIL;
18182 List *oids_to_drop = NIL;
18184 foreach(l, on_commits)
18186 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18188 /* Ignore entry if already dropped in this xact */
18189 if (oc->deleting_subid != InvalidSubTransactionId)
18190 continue;
18192 switch (oc->oncommit)
18194 case ONCOMMIT_NOOP:
18195 case ONCOMMIT_PRESERVE_ROWS:
18196 /* Do nothing (there shouldn't be such entries, actually) */
18197 break;
18198 case ONCOMMIT_DELETE_ROWS:
18201 * If this transaction hasn't accessed any temporary
18202 * relations, we can skip truncating ON COMMIT DELETE ROWS
18203 * tables, as they must still be empty.
18205 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18206 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18207 break;
18208 case ONCOMMIT_DROP:
18209 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18210 break;
18215 * Truncate relations before dropping so that all dependencies between
18216 * relations are removed after they are worked on. Doing it like this
18217 * might be a waste as it is possible that a relation being truncated will
18218 * be dropped anyway due to its parent being dropped, but this makes the
18219 * code more robust because of not having to re-check that the relation
18220 * exists at truncation time.
18222 if (oids_to_truncate != NIL)
18223 heap_truncate(oids_to_truncate);
18225 if (oids_to_drop != NIL)
18227 ObjectAddresses *targetObjects = new_object_addresses();
18229 foreach(l, oids_to_drop)
18231 ObjectAddress object;
18233 object.classId = RelationRelationId;
18234 object.objectId = lfirst_oid(l);
18235 object.objectSubId = 0;
18237 Assert(!object_address_present(&object, targetObjects));
18239 add_exact_object_address(&object, targetObjects);
18243 * Object deletion might involve toast table access (to clean up
18244 * toasted catalog entries), so ensure we have a valid snapshot.
18246 PushActiveSnapshot(GetTransactionSnapshot());
18249 * Since this is an automatic drop, rather than one directly initiated
18250 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18252 performMultipleDeletions(targetObjects, DROP_CASCADE,
18253 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18255 PopActiveSnapshot();
18257 #ifdef USE_ASSERT_CHECKING
18260 * Note that table deletion will call remove_on_commit_action, so the
18261 * entry should get marked as deleted.
18263 foreach(l, on_commits)
18265 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18267 if (oc->oncommit != ONCOMMIT_DROP)
18268 continue;
18270 Assert(oc->deleting_subid != InvalidSubTransactionId);
18272 #endif
18277 * Post-commit or post-abort cleanup for ON COMMIT management.
18279 * All we do here is remove no-longer-needed OnCommitItem entries.
18281 * During commit, remove entries that were deleted during this transaction;
18282 * during abort, remove those created during this transaction.
18284 void
18285 AtEOXact_on_commit_actions(bool isCommit)
18287 ListCell *cur_item;
18289 foreach(cur_item, on_commits)
18291 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18293 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18294 oc->creating_subid != InvalidSubTransactionId)
18296 /* cur_item must be removed */
18297 on_commits = foreach_delete_current(on_commits, cur_item);
18298 pfree(oc);
18300 else
18302 /* cur_item must be preserved */
18303 oc->creating_subid = InvalidSubTransactionId;
18304 oc->deleting_subid = InvalidSubTransactionId;
18310 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18312 * During subabort, we can immediately remove entries created during this
18313 * subtransaction. During subcommit, just relabel entries marked during
18314 * this subtransaction as being the parent's responsibility.
18316 void
18317 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18318 SubTransactionId parentSubid)
18320 ListCell *cur_item;
18322 foreach(cur_item, on_commits)
18324 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18326 if (!isCommit && oc->creating_subid == mySubid)
18328 /* cur_item must be removed */
18329 on_commits = foreach_delete_current(on_commits, cur_item);
18330 pfree(oc);
18332 else
18334 /* cur_item must be preserved */
18335 if (oc->creating_subid == mySubid)
18336 oc->creating_subid = parentSubid;
18337 if (oc->deleting_subid == mySubid)
18338 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18344 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18345 * the relation to be locked only if (1) it's a plain or partitioned table,
18346 * materialized view, or TOAST table and (2) the current user is the owner (or
18347 * the superuser) or has been granted MAINTAIN. This meets the
18348 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18349 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18351 void
18352 RangeVarCallbackMaintainsTable(const RangeVar *relation,
18353 Oid relId, Oid oldRelId, void *arg)
18355 char relkind;
18356 AclResult aclresult;
18358 /* Nothing to do if the relation was not found. */
18359 if (!OidIsValid(relId))
18360 return;
18363 * If the relation does exist, check whether it's an index. But note that
18364 * the relation might have been dropped between the time we did the name
18365 * lookup and now. In that case, there's nothing to do.
18367 relkind = get_rel_relkind(relId);
18368 if (!relkind)
18369 return;
18370 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18371 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18372 ereport(ERROR,
18373 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18374 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18376 /* Check permissions */
18377 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18378 if (aclresult != ACLCHECK_OK)
18379 aclcheck_error(aclresult,
18380 get_relkind_objtype(get_rel_relkind(relId)),
18381 relation->relname);
18385 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18387 static void
18388 RangeVarCallbackForTruncate(const RangeVar *relation,
18389 Oid relId, Oid oldRelId, void *arg)
18391 HeapTuple tuple;
18393 /* Nothing to do if the relation was not found. */
18394 if (!OidIsValid(relId))
18395 return;
18397 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18398 if (!HeapTupleIsValid(tuple)) /* should not happen */
18399 elog(ERROR, "cache lookup failed for relation %u", relId);
18401 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18402 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18404 ReleaseSysCache(tuple);
18408 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18409 * the owner of the relation, or superuser.
18411 void
18412 RangeVarCallbackOwnsRelation(const RangeVar *relation,
18413 Oid relId, Oid oldRelId, void *arg)
18415 HeapTuple tuple;
18417 /* Nothing to do if the relation was not found. */
18418 if (!OidIsValid(relId))
18419 return;
18421 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18422 if (!HeapTupleIsValid(tuple)) /* should not happen */
18423 elog(ERROR, "cache lookup failed for relation %u", relId);
18425 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18426 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18427 relation->relname);
18429 if (!allowSystemTableMods &&
18430 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18431 ereport(ERROR,
18432 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18433 errmsg("permission denied: \"%s\" is a system catalog",
18434 relation->relname)));
18436 ReleaseSysCache(tuple);
18440 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18441 * processing.
18443 static void
18444 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18445 void *arg)
18447 Node *stmt = (Node *) arg;
18448 ObjectType reltype;
18449 HeapTuple tuple;
18450 Form_pg_class classform;
18451 AclResult aclresult;
18452 char relkind;
18454 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18455 if (!HeapTupleIsValid(tuple))
18456 return; /* concurrently dropped */
18457 classform = (Form_pg_class) GETSTRUCT(tuple);
18458 relkind = classform->relkind;
18460 /* Must own relation. */
18461 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18462 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18464 /* No system table modifications unless explicitly allowed. */
18465 if (!allowSystemTableMods && IsSystemClass(relid, classform))
18466 ereport(ERROR,
18467 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18468 errmsg("permission denied: \"%s\" is a system catalog",
18469 rv->relname)));
18472 * Extract the specified relation type from the statement parse tree.
18474 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18475 * have CREATE rights on the containing namespace.
18477 if (IsA(stmt, RenameStmt))
18479 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18480 GetUserId(), ACL_CREATE);
18481 if (aclresult != ACLCHECK_OK)
18482 aclcheck_error(aclresult, OBJECT_SCHEMA,
18483 get_namespace_name(classform->relnamespace));
18484 reltype = ((RenameStmt *) stmt)->renameType;
18486 else if (IsA(stmt, AlterObjectSchemaStmt))
18487 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18489 else if (IsA(stmt, AlterTableStmt))
18490 reltype = ((AlterTableStmt *) stmt)->objtype;
18491 else
18493 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18494 reltype = OBJECT_TABLE; /* placate compiler */
18498 * For compatibility with prior releases, we allow ALTER TABLE to be used
18499 * with most other types of relations (but not composite types). We allow
18500 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18501 * otherwise. Otherwise, the user must select the correct form of the
18502 * command for the relation at issue.
18504 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18505 ereport(ERROR,
18506 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18507 errmsg("\"%s\" is not a sequence", rv->relname)));
18509 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18510 ereport(ERROR,
18511 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18512 errmsg("\"%s\" is not a view", rv->relname)));
18514 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18515 ereport(ERROR,
18516 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18517 errmsg("\"%s\" is not a materialized view", rv->relname)));
18519 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18520 ereport(ERROR,
18521 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18522 errmsg("\"%s\" is not a foreign table", rv->relname)));
18524 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18525 ereport(ERROR,
18526 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18527 errmsg("\"%s\" is not a composite type", rv->relname)));
18529 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18530 relkind != RELKIND_PARTITIONED_INDEX
18531 && !IsA(stmt, RenameStmt))
18532 ereport(ERROR,
18533 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18534 errmsg("\"%s\" is not an index", rv->relname)));
18537 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18538 * TYPE for that.
18540 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18541 ereport(ERROR,
18542 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18543 errmsg("\"%s\" is a composite type", rv->relname),
18544 /* translator: %s is an SQL ALTER command */
18545 errhint("Use %s instead.",
18546 "ALTER TYPE")));
18549 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18550 * to a different schema, such as indexes and TOAST tables.
18552 if (IsA(stmt, AlterObjectSchemaStmt))
18554 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18555 ereport(ERROR,
18556 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18557 errmsg("cannot change schema of index \"%s\"",
18558 rv->relname),
18559 errhint("Change the schema of the table instead.")));
18560 else if (relkind == RELKIND_COMPOSITE_TYPE)
18561 ereport(ERROR,
18562 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18563 errmsg("cannot change schema of composite type \"%s\"",
18564 rv->relname),
18565 /* translator: %s is an SQL ALTER command */
18566 errhint("Use %s instead.",
18567 "ALTER TYPE")));
18568 else if (relkind == RELKIND_TOASTVALUE)
18569 ereport(ERROR,
18570 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18571 errmsg("cannot change schema of TOAST table \"%s\"",
18572 rv->relname),
18573 errhint("Change the schema of the table instead.")));
18576 ReleaseSysCache(tuple);
18580 * Transform any expressions present in the partition key
18582 * Returns a transformed PartitionSpec.
18584 static PartitionSpec *
18585 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18587 PartitionSpec *newspec;
18588 ParseState *pstate;
18589 ParseNamespaceItem *nsitem;
18590 ListCell *l;
18592 newspec = makeNode(PartitionSpec);
18594 newspec->strategy = partspec->strategy;
18595 newspec->partParams = NIL;
18596 newspec->location = partspec->location;
18598 /* Check valid number of columns for strategy */
18599 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18600 list_length(partspec->partParams) != 1)
18601 ereport(ERROR,
18602 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18603 errmsg("cannot use \"list\" partition strategy with more than one column")));
18606 * Create a dummy ParseState and insert the target relation as its sole
18607 * rangetable entry. We need a ParseState for transformExpr.
18609 pstate = make_parsestate(NULL);
18610 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18611 NULL, false, true);
18612 addNSItemToQuery(pstate, nsitem, true, true, true);
18614 /* take care of any partition expressions */
18615 foreach(l, partspec->partParams)
18617 PartitionElem *pelem = lfirst_node(PartitionElem, l);
18619 if (pelem->expr)
18621 /* Copy, to avoid scribbling on the input */
18622 pelem = copyObject(pelem);
18624 /* Now do parse transformation of the expression */
18625 pelem->expr = transformExpr(pstate, pelem->expr,
18626 EXPR_KIND_PARTITION_EXPRESSION);
18628 /* we have to fix its collations too */
18629 assign_expr_collations(pstate, pelem->expr);
18632 newspec->partParams = lappend(newspec->partParams, pelem);
18635 return newspec;
18639 * Compute per-partition-column information from a list of PartitionElems.
18640 * Expressions in the PartitionElems must be parse-analyzed already.
18642 static void
18643 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18644 List **partexprs, Oid *partopclass, Oid *partcollation,
18645 PartitionStrategy strategy)
18647 int attn;
18648 ListCell *lc;
18649 Oid am_oid;
18651 attn = 0;
18652 foreach(lc, partParams)
18654 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18655 Oid atttype;
18656 Oid attcollation;
18658 if (pelem->name != NULL)
18660 /* Simple attribute reference */
18661 HeapTuple atttuple;
18662 Form_pg_attribute attform;
18664 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18665 pelem->name);
18666 if (!HeapTupleIsValid(atttuple))
18667 ereport(ERROR,
18668 (errcode(ERRCODE_UNDEFINED_COLUMN),
18669 errmsg("column \"%s\" named in partition key does not exist",
18670 pelem->name),
18671 parser_errposition(pstate, pelem->location)));
18672 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18674 if (attform->attnum <= 0)
18675 ereport(ERROR,
18676 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18677 errmsg("cannot use system column \"%s\" in partition key",
18678 pelem->name),
18679 parser_errposition(pstate, pelem->location)));
18682 * Generated columns cannot work: They are computed after BEFORE
18683 * triggers, but partition routing is done before all triggers.
18685 if (attform->attgenerated)
18686 ereport(ERROR,
18687 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18688 errmsg("cannot use generated column in partition key"),
18689 errdetail("Column \"%s\" is a generated column.",
18690 pelem->name),
18691 parser_errposition(pstate, pelem->location)));
18693 partattrs[attn] = attform->attnum;
18694 atttype = attform->atttypid;
18695 attcollation = attform->attcollation;
18696 ReleaseSysCache(atttuple);
18698 else
18700 /* Expression */
18701 Node *expr = pelem->expr;
18702 char partattname[16];
18704 Assert(expr != NULL);
18705 atttype = exprType(expr);
18706 attcollation = exprCollation(expr);
18709 * The expression must be of a storable type (e.g., not RECORD).
18710 * The test is the same as for whether a table column is of a safe
18711 * type (which is why we needn't check for the non-expression
18712 * case).
18714 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18715 CheckAttributeType(partattname,
18716 atttype, attcollation,
18717 NIL, CHKATYPE_IS_PARTKEY);
18720 * Strip any top-level COLLATE clause. This ensures that we treat
18721 * "x COLLATE y" and "(x COLLATE y)" alike.
18723 while (IsA(expr, CollateExpr))
18724 expr = (Node *) ((CollateExpr *) expr)->arg;
18726 if (IsA(expr, Var) &&
18727 ((Var *) expr)->varattno > 0)
18730 * User wrote "(column)" or "(column COLLATE something)".
18731 * Treat it like simple attribute anyway.
18733 partattrs[attn] = ((Var *) expr)->varattno;
18735 else
18737 Bitmapset *expr_attrs = NULL;
18738 int i;
18740 partattrs[attn] = 0; /* marks the column as expression */
18741 *partexprs = lappend(*partexprs, expr);
18744 * transformPartitionSpec() should have already rejected
18745 * subqueries, aggregates, window functions, and SRFs, based
18746 * on the EXPR_KIND_ for partition expressions.
18750 * Cannot allow system column references, since that would
18751 * make partition routing impossible: their values won't be
18752 * known yet when we need to do that.
18754 pull_varattnos(expr, 1, &expr_attrs);
18755 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18757 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18758 expr_attrs))
18759 ereport(ERROR,
18760 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18761 errmsg("partition key expressions cannot contain system column references")));
18765 * Generated columns cannot work: They are computed after
18766 * BEFORE triggers, but partition routing is done before all
18767 * triggers.
18769 i = -1;
18770 while ((i = bms_next_member(expr_attrs, i)) >= 0)
18772 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18774 if (attno > 0 &&
18775 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18776 ereport(ERROR,
18777 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18778 errmsg("cannot use generated column in partition key"),
18779 errdetail("Column \"%s\" is a generated column.",
18780 get_attname(RelationGetRelid(rel), attno, false)),
18781 parser_errposition(pstate, pelem->location)));
18785 * Preprocess the expression before checking for mutability.
18786 * This is essential for the reasons described in
18787 * contain_mutable_functions_after_planning. However, we call
18788 * expression_planner for ourselves rather than using that
18789 * function, because if constant-folding reduces the
18790 * expression to a constant, we'd like to know that so we can
18791 * complain below.
18793 * Like contain_mutable_functions_after_planning, assume that
18794 * expression_planner won't scribble on its input, so this
18795 * won't affect the partexprs entry we saved above.
18797 expr = (Node *) expression_planner((Expr *) expr);
18800 * Partition expressions cannot contain mutable functions,
18801 * because a given row must always map to the same partition
18802 * as long as there is no change in the partition boundary
18803 * structure.
18805 if (contain_mutable_functions(expr))
18806 ereport(ERROR,
18807 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18808 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18811 * While it is not exactly *wrong* for a partition expression
18812 * to be a constant, it seems better to reject such keys.
18814 if (IsA(expr, Const))
18815 ereport(ERROR,
18816 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18817 errmsg("cannot use constant expression as partition key")));
18822 * Apply collation override if any
18824 if (pelem->collation)
18825 attcollation = get_collation_oid(pelem->collation, false);
18828 * Check we have a collation iff it's a collatable type. The only
18829 * expected failures here are (1) COLLATE applied to a noncollatable
18830 * type, or (2) partition expression had an unresolved collation. But
18831 * we might as well code this to be a complete consistency check.
18833 if (type_is_collatable(atttype))
18835 if (!OidIsValid(attcollation))
18836 ereport(ERROR,
18837 (errcode(ERRCODE_INDETERMINATE_COLLATION),
18838 errmsg("could not determine which collation to use for partition expression"),
18839 errhint("Use the COLLATE clause to set the collation explicitly.")));
18841 else
18843 if (OidIsValid(attcollation))
18844 ereport(ERROR,
18845 (errcode(ERRCODE_DATATYPE_MISMATCH),
18846 errmsg("collations are not supported by type %s",
18847 format_type_be(atttype))));
18850 partcollation[attn] = attcollation;
18853 * Identify the appropriate operator class. For list and range
18854 * partitioning, we use a btree operator class; hash partitioning uses
18855 * a hash operator class.
18857 if (strategy == PARTITION_STRATEGY_HASH)
18858 am_oid = HASH_AM_OID;
18859 else
18860 am_oid = BTREE_AM_OID;
18862 if (!pelem->opclass)
18864 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18866 if (!OidIsValid(partopclass[attn]))
18868 if (strategy == PARTITION_STRATEGY_HASH)
18869 ereport(ERROR,
18870 (errcode(ERRCODE_UNDEFINED_OBJECT),
18871 errmsg("data type %s has no default operator class for access method \"%s\"",
18872 format_type_be(atttype), "hash"),
18873 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18874 else
18875 ereport(ERROR,
18876 (errcode(ERRCODE_UNDEFINED_OBJECT),
18877 errmsg("data type %s has no default operator class for access method \"%s\"",
18878 format_type_be(atttype), "btree"),
18879 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18882 else
18883 partopclass[attn] = ResolveOpClass(pelem->opclass,
18884 atttype,
18885 am_oid == HASH_AM_OID ? "hash" : "btree",
18886 am_oid);
18888 attn++;
18893 * PartConstraintImpliedByRelConstraint
18894 * Do scanrel's existing constraints imply the partition constraint?
18896 * "Existing constraints" include its check constraints and column-level
18897 * not-null constraints. partConstraint describes the partition constraint,
18898 * in implicit-AND form.
18900 bool
18901 PartConstraintImpliedByRelConstraint(Relation scanrel,
18902 List *partConstraint)
18904 List *existConstraint = NIL;
18905 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18906 int i;
18908 if (constr && constr->has_not_null)
18910 int natts = scanrel->rd_att->natts;
18912 for (i = 1; i <= natts; i++)
18914 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18916 if (att->attnotnull && !att->attisdropped)
18918 NullTest *ntest = makeNode(NullTest);
18920 ntest->arg = (Expr *) makeVar(1,
18922 att->atttypid,
18923 att->atttypmod,
18924 att->attcollation,
18926 ntest->nulltesttype = IS_NOT_NULL;
18929 * argisrow=false is correct even for a composite column,
18930 * because attnotnull does not represent a SQL-spec IS NOT
18931 * NULL test in such a case, just IS DISTINCT FROM NULL.
18933 ntest->argisrow = false;
18934 ntest->location = -1;
18935 existConstraint = lappend(existConstraint, ntest);
18940 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18944 * ConstraintImpliedByRelConstraint
18945 * Do scanrel's existing constraints imply the given constraint?
18947 * testConstraint is the constraint to validate. provenConstraint is a
18948 * caller-provided list of conditions which this function may assume
18949 * to be true. Both provenConstraint and testConstraint must be in
18950 * implicit-AND form, must only contain immutable clauses, and must
18951 * contain only Vars with varno = 1.
18953 bool
18954 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18956 List *existConstraint = list_copy(provenConstraint);
18957 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18958 int num_check,
18961 num_check = (constr != NULL) ? constr->num_check : 0;
18962 for (i = 0; i < num_check; i++)
18964 Node *cexpr;
18967 * If this constraint hasn't been fully validated yet, we must ignore
18968 * it here.
18970 if (!constr->check[i].ccvalid)
18971 continue;
18973 cexpr = stringToNode(constr->check[i].ccbin);
18976 * Run each expression through const-simplification and
18977 * canonicalization. It is necessary, because we will be comparing it
18978 * to similarly-processed partition constraint expressions, and may
18979 * fail to detect valid matches without this.
18981 cexpr = eval_const_expressions(NULL, cexpr);
18982 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18984 existConstraint = list_concat(existConstraint,
18985 make_ands_implicit((Expr *) cexpr));
18989 * Try to make the proof. Since we are comparing CHECK constraints, we
18990 * need to use weak implication, i.e., we assume existConstraint is
18991 * not-false and try to prove the same for testConstraint.
18993 * Note that predicate_implied_by assumes its first argument is known
18994 * immutable. That should always be true for both NOT NULL and partition
18995 * constraints, so we don't test it here.
18997 return predicate_implied_by(testConstraint, existConstraint, true);
19001 * QueuePartitionConstraintValidation
19003 * Add an entry to wqueue to have the given partition constraint validated by
19004 * Phase 3, for the given relation, and all its children.
19006 * We first verify whether the given constraint is implied by pre-existing
19007 * relation constraints; if it is, there's no need to scan the table to
19008 * validate, so don't queue in that case.
19010 static void
19011 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19012 List *partConstraint,
19013 bool validate_default)
19016 * Based on the table's existing constraints, determine whether or not we
19017 * may skip scanning the table.
19019 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19021 if (!validate_default)
19022 ereport(DEBUG1,
19023 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19024 RelationGetRelationName(scanrel))));
19025 else
19026 ereport(DEBUG1,
19027 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19028 RelationGetRelationName(scanrel))));
19029 return;
19033 * Constraints proved insufficient. For plain relations, queue a
19034 * validation item now; for partitioned tables, recurse to process each
19035 * partition.
19037 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19039 AlteredTableInfo *tab;
19041 /* Grab a work queue entry. */
19042 tab = ATGetQueueEntry(wqueue, scanrel);
19043 Assert(tab->partition_constraint == NULL);
19044 tab->partition_constraint = (Expr *) linitial(partConstraint);
19045 tab->validate_default = validate_default;
19047 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19049 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19050 int i;
19052 for (i = 0; i < partdesc->nparts; i++)
19054 Relation part_rel;
19055 List *thisPartConstraint;
19058 * This is the minimum lock we need to prevent deadlocks.
19060 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19063 * Adjust the constraint for scanrel so that it matches this
19064 * partition's attribute numbers.
19066 thisPartConstraint =
19067 map_partition_varattnos(partConstraint, 1,
19068 part_rel, scanrel);
19070 QueuePartitionConstraintValidation(wqueue, part_rel,
19071 thisPartConstraint,
19072 validate_default);
19073 table_close(part_rel, NoLock); /* keep lock till commit */
19079 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19081 * Return the address of the newly attached partition.
19083 static ObjectAddress
19084 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19085 AlterTableUtilityContext *context)
19087 Relation attachrel,
19088 catalog;
19089 List *attachrel_children;
19090 List *partConstraint;
19091 SysScanDesc scan;
19092 ScanKeyData skey;
19093 AttrNumber attno;
19094 int natts;
19095 TupleDesc tupleDesc;
19096 ObjectAddress address;
19097 const char *trigger_name;
19098 Oid defaultPartOid;
19099 List *partBoundConstraint;
19100 ParseState *pstate = make_parsestate(NULL);
19102 pstate->p_sourcetext = context->queryString;
19105 * We must lock the default partition if one exists, because attaching a
19106 * new partition will change its partition constraint.
19108 defaultPartOid =
19109 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19110 if (OidIsValid(defaultPartOid))
19111 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19113 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19116 * XXX I think it'd be a good idea to grab locks on all tables referenced
19117 * by FKs at this point also.
19121 * Must be owner of both parent and source table -- parent was checked by
19122 * ATSimplePermissions call in ATPrepCmd
19124 ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
19126 /* A partition can only have one parent */
19127 if (attachrel->rd_rel->relispartition)
19128 ereport(ERROR,
19129 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19130 errmsg("\"%s\" is already a partition",
19131 RelationGetRelationName(attachrel))));
19133 if (OidIsValid(attachrel->rd_rel->reloftype))
19134 ereport(ERROR,
19135 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19136 errmsg("cannot attach a typed table as partition")));
19139 * Table being attached should not already be part of inheritance; either
19140 * as a child table...
19142 catalog = table_open(InheritsRelationId, AccessShareLock);
19143 ScanKeyInit(&skey,
19144 Anum_pg_inherits_inhrelid,
19145 BTEqualStrategyNumber, F_OIDEQ,
19146 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19147 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19148 NULL, 1, &skey);
19149 if (HeapTupleIsValid(systable_getnext(scan)))
19150 ereport(ERROR,
19151 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19152 errmsg("cannot attach inheritance child as partition")));
19153 systable_endscan(scan);
19155 /* ...or as a parent table (except the case when it is partitioned) */
19156 ScanKeyInit(&skey,
19157 Anum_pg_inherits_inhparent,
19158 BTEqualStrategyNumber, F_OIDEQ,
19159 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19160 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19161 1, &skey);
19162 if (HeapTupleIsValid(systable_getnext(scan)) &&
19163 attachrel->rd_rel->relkind == RELKIND_RELATION)
19164 ereport(ERROR,
19165 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19166 errmsg("cannot attach inheritance parent as partition")));
19167 systable_endscan(scan);
19168 table_close(catalog, AccessShareLock);
19171 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19172 * particular, this disallows making a rel a partition of itself.)
19174 * We do that by checking if rel is a member of the list of attachrel's
19175 * partitions provided the latter is partitioned at all. We want to avoid
19176 * having to construct this list again, so we request the strongest lock
19177 * on all partitions. We need the strongest lock, because we may decide
19178 * to scan them if we find out that the table being attached (or its leaf
19179 * partitions) may contain rows that violate the partition constraint. If
19180 * the table has a constraint that would prevent such rows, which by
19181 * definition is present in all the partitions, we need not scan the
19182 * table, nor its partitions. But we cannot risk a deadlock by taking a
19183 * weaker lock now and the stronger one only when needed.
19185 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19186 AccessExclusiveLock, NULL);
19187 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19188 ereport(ERROR,
19189 (errcode(ERRCODE_DUPLICATE_TABLE),
19190 errmsg("circular inheritance not allowed"),
19191 errdetail("\"%s\" is already a child of \"%s\".",
19192 RelationGetRelationName(rel),
19193 RelationGetRelationName(attachrel))));
19195 /* If the parent is permanent, so must be all of its partitions. */
19196 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19197 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19198 ereport(ERROR,
19199 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19200 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19201 RelationGetRelationName(rel))));
19203 /* Temp parent cannot have a partition that is itself not a temp */
19204 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19205 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19206 ereport(ERROR,
19207 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19208 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19209 RelationGetRelationName(rel))));
19211 /* If the parent is temp, it must belong to this session */
19212 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19213 !rel->rd_islocaltemp)
19214 ereport(ERROR,
19215 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19216 errmsg("cannot attach as partition of temporary relation of another session")));
19218 /* Ditto for the partition */
19219 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19220 !attachrel->rd_islocaltemp)
19221 ereport(ERROR,
19222 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19223 errmsg("cannot attach temporary relation of another session as partition")));
19226 * Check if attachrel has any identity columns or any columns that aren't
19227 * in the parent.
19229 tupleDesc = RelationGetDescr(attachrel);
19230 natts = tupleDesc->natts;
19231 for (attno = 1; attno <= natts; attno++)
19233 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19234 char *attributeName = NameStr(attribute->attname);
19236 /* Ignore dropped */
19237 if (attribute->attisdropped)
19238 continue;
19240 if (attribute->attidentity)
19241 ereport(ERROR,
19242 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19243 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19244 RelationGetRelationName(attachrel), attributeName),
19245 errdetail("The new partition may not contain an identity column."));
19247 /* Try to find the column in parent (matching on column name) */
19248 if (!SearchSysCacheExists2(ATTNAME,
19249 ObjectIdGetDatum(RelationGetRelid(rel)),
19250 CStringGetDatum(attributeName)))
19251 ereport(ERROR,
19252 (errcode(ERRCODE_DATATYPE_MISMATCH),
19253 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19254 RelationGetRelationName(attachrel), attributeName,
19255 RelationGetRelationName(rel)),
19256 errdetail("The new partition may contain only the columns present in parent.")));
19260 * If child_rel has row-level triggers with transition tables, we
19261 * currently don't allow it to become a partition. See also prohibitions
19262 * in ATExecAddInherit() and CreateTrigger().
19264 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19265 if (trigger_name != NULL)
19266 ereport(ERROR,
19267 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19268 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19269 trigger_name, RelationGetRelationName(attachrel)),
19270 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19273 * Check that the new partition's bound is valid and does not overlap any
19274 * of existing partitions of the parent - note that it does not return on
19275 * error.
19277 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19278 cmd->bound, pstate);
19280 /* OK to create inheritance. Rest of the checks performed there */
19281 CreateInheritance(attachrel, rel, true);
19283 /* Update the pg_class entry. */
19284 StorePartitionBound(attachrel, rel, cmd->bound);
19286 /* Ensure there exists a correct set of indexes in the partition. */
19287 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19289 /* and triggers */
19290 CloneRowTriggersToPartition(rel, attachrel);
19293 * Clone foreign key constraints. Callee is responsible for setting up
19294 * for phase 3 constraint verification.
19296 CloneForeignKeyConstraints(wqueue, rel, attachrel);
19299 * Generate partition constraint from the partition bound specification.
19300 * If the parent itself is a partition, make sure to include its
19301 * constraint as well.
19303 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19304 partConstraint = list_concat(partBoundConstraint,
19305 RelationGetPartitionQual(rel));
19307 /* Skip validation if there are no constraints to validate. */
19308 if (partConstraint)
19311 * Run the partition quals through const-simplification similar to
19312 * check constraints. We skip canonicalize_qual, though, because
19313 * partition quals should be in canonical form already.
19315 partConstraint =
19316 (List *) eval_const_expressions(NULL,
19317 (Node *) partConstraint);
19319 /* XXX this sure looks wrong */
19320 partConstraint = list_make1(make_ands_explicit(partConstraint));
19323 * Adjust the generated constraint to match this partition's attribute
19324 * numbers.
19326 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19327 rel);
19329 /* Validate partition constraints against the table being attached. */
19330 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19331 false);
19335 * If we're attaching a partition other than the default partition and a
19336 * default one exists, then that partition's partition constraint changes,
19337 * so add an entry to the work queue to validate it, too. (We must not do
19338 * this when the partition being attached is the default one; we already
19339 * did it above!)
19341 if (OidIsValid(defaultPartOid))
19343 Relation defaultrel;
19344 List *defPartConstraint;
19346 Assert(!cmd->bound->is_default);
19348 /* we already hold a lock on the default partition */
19349 defaultrel = table_open(defaultPartOid, NoLock);
19350 defPartConstraint =
19351 get_proposed_default_constraint(partBoundConstraint);
19354 * Map the Vars in the constraint expression from rel's attnos to
19355 * defaultrel's.
19357 defPartConstraint =
19358 map_partition_varattnos(defPartConstraint,
19359 1, defaultrel, rel);
19360 QueuePartitionConstraintValidation(wqueue, defaultrel,
19361 defPartConstraint, true);
19363 /* keep our lock until commit. */
19364 table_close(defaultrel, NoLock);
19367 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19370 * If the partition we just attached is partitioned itself, invalidate
19371 * relcache for all descendent partitions too to ensure that their
19372 * rd_partcheck expression trees are rebuilt; partitions already locked at
19373 * the beginning of this function.
19375 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19377 ListCell *l;
19379 foreach(l, attachrel_children)
19381 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19385 /* keep our lock until commit */
19386 table_close(attachrel, NoLock);
19388 return address;
19392 * AttachPartitionEnsureIndexes
19393 * subroutine for ATExecAttachPartition to create/match indexes
19395 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19396 * PARTITION: every partition must have an index attached to each index on the
19397 * partitioned table.
19399 static void
19400 AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19402 List *idxes;
19403 List *attachRelIdxs;
19404 Relation *attachrelIdxRels;
19405 IndexInfo **attachInfos;
19406 ListCell *cell;
19407 MemoryContext cxt;
19408 MemoryContext oldcxt;
19410 cxt = AllocSetContextCreate(CurrentMemoryContext,
19411 "AttachPartitionEnsureIndexes",
19412 ALLOCSET_DEFAULT_SIZES);
19413 oldcxt = MemoryContextSwitchTo(cxt);
19415 idxes = RelationGetIndexList(rel);
19416 attachRelIdxs = RelationGetIndexList(attachrel);
19417 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19418 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19420 /* Build arrays of all existing indexes and their IndexInfos */
19421 foreach(cell, attachRelIdxs)
19423 Oid cldIdxId = lfirst_oid(cell);
19424 int i = foreach_current_index(cell);
19426 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19427 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19431 * If we're attaching a foreign table, we must fail if any of the indexes
19432 * is a constraint index; otherwise, there's nothing to do here. Do this
19433 * before starting work, to avoid wasting the effort of building a few
19434 * non-unique indexes before coming across a unique one.
19436 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19438 foreach(cell, idxes)
19440 Oid idx = lfirst_oid(cell);
19441 Relation idxRel = index_open(idx, AccessShareLock);
19443 if (idxRel->rd_index->indisunique ||
19444 idxRel->rd_index->indisprimary)
19445 ereport(ERROR,
19446 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19447 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19448 RelationGetRelationName(attachrel),
19449 RelationGetRelationName(rel)),
19450 errdetail("Partitioned table \"%s\" contains unique indexes.",
19451 RelationGetRelationName(rel))));
19452 index_close(idxRel, AccessShareLock);
19455 goto out;
19459 * For each index on the partitioned table, find a matching one in the
19460 * partition-to-be; if one is not found, create one.
19462 foreach(cell, idxes)
19464 Oid idx = lfirst_oid(cell);
19465 Relation idxRel = index_open(idx, AccessShareLock);
19466 IndexInfo *info;
19467 AttrMap *attmap;
19468 bool found = false;
19469 Oid constraintOid;
19472 * Ignore indexes in the partitioned table other than partitioned
19473 * indexes.
19475 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19477 index_close(idxRel, AccessShareLock);
19478 continue;
19481 /* construct an indexinfo to compare existing indexes against */
19482 info = BuildIndexInfo(idxRel);
19483 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19484 RelationGetDescr(rel),
19485 false);
19486 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19489 * Scan the list of existing indexes in the partition-to-be, and mark
19490 * the first matching, valid, unattached one we find, if any, as
19491 * partition of the parent index. If we find one, we're done.
19493 for (int i = 0; i < list_length(attachRelIdxs); i++)
19495 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19496 Oid cldConstrOid = InvalidOid;
19498 /* does this index have a parent? if so, can't use it */
19499 if (attachrelIdxRels[i]->rd_rel->relispartition)
19500 continue;
19502 /* If this index is invalid, can't use it */
19503 if (!attachrelIdxRels[i]->rd_index->indisvalid)
19504 continue;
19506 if (CompareIndexInfo(attachInfos[i], info,
19507 attachrelIdxRels[i]->rd_indcollation,
19508 idxRel->rd_indcollation,
19509 attachrelIdxRels[i]->rd_opfamily,
19510 idxRel->rd_opfamily,
19511 attmap))
19514 * If this index is being created in the parent because of a
19515 * constraint, then the child needs to have a constraint also,
19516 * so look for one. If there is no such constraint, this
19517 * index is no good, so keep looking.
19519 if (OidIsValid(constraintOid))
19521 cldConstrOid =
19522 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19523 cldIdxId);
19524 /* no dice */
19525 if (!OidIsValid(cldConstrOid))
19526 continue;
19529 /* bingo. */
19530 IndexSetParentIndex(attachrelIdxRels[i], idx);
19531 if (OidIsValid(constraintOid))
19532 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19533 RelationGetRelid(attachrel));
19534 found = true;
19536 CommandCounterIncrement();
19537 break;
19542 * If no suitable index was found in the partition-to-be, create one
19543 * now.
19545 if (!found)
19547 IndexStmt *stmt;
19548 Oid conOid;
19550 stmt = generateClonedIndexStmt(NULL,
19551 idxRel, attmap,
19552 &conOid);
19555 * If the index is a primary key, mark all columns as NOT NULL if
19556 * they aren't already.
19558 if (stmt->primary)
19560 MemoryContextSwitchTo(oldcxt);
19561 for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19563 AttrNumber childattno;
19565 childattno = get_attnum(RelationGetRelid(attachrel),
19566 get_attname(RelationGetRelid(rel),
19567 info->ii_IndexAttrNumbers[j],
19568 false));
19569 set_attnotnull(wqueue, attachrel, childattno,
19570 true, AccessExclusiveLock);
19572 MemoryContextSwitchTo(cxt);
19575 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19576 RelationGetRelid(idxRel),
19577 conOid,
19579 true, false, false, false, false);
19582 index_close(idxRel, AccessShareLock);
19585 out:
19586 /* Clean up. */
19587 for (int i = 0; i < list_length(attachRelIdxs); i++)
19588 index_close(attachrelIdxRels[i], AccessShareLock);
19589 MemoryContextSwitchTo(oldcxt);
19590 MemoryContextDelete(cxt);
19594 * CloneRowTriggersToPartition
19595 * subroutine for ATExecAttachPartition/DefineRelation to create row
19596 * triggers on partitions
19598 static void
19599 CloneRowTriggersToPartition(Relation parent, Relation partition)
19601 Relation pg_trigger;
19602 ScanKeyData key;
19603 SysScanDesc scan;
19604 HeapTuple tuple;
19605 MemoryContext perTupCxt;
19607 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19608 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19609 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19610 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19611 true, NULL, 1, &key);
19613 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19614 "clone trig", ALLOCSET_SMALL_SIZES);
19616 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19618 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19619 CreateTrigStmt *trigStmt;
19620 Node *qual = NULL;
19621 Datum value;
19622 bool isnull;
19623 List *cols = NIL;
19624 List *trigargs = NIL;
19625 MemoryContext oldcxt;
19628 * Ignore statement-level triggers; those are not cloned.
19630 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19631 continue;
19634 * Don't clone internal triggers, because the constraint cloning code
19635 * will.
19637 if (trigForm->tgisinternal)
19638 continue;
19641 * Complain if we find an unexpected trigger type.
19643 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19644 !TRIGGER_FOR_AFTER(trigForm->tgtype))
19645 elog(ERROR, "unexpected trigger \"%s\" found",
19646 NameStr(trigForm->tgname));
19648 /* Use short-lived context for CREATE TRIGGER */
19649 oldcxt = MemoryContextSwitchTo(perTupCxt);
19652 * If there is a WHEN clause, generate a 'cooked' version of it that's
19653 * appropriate for the partition.
19655 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19656 RelationGetDescr(pg_trigger), &isnull);
19657 if (!isnull)
19659 qual = stringToNode(TextDatumGetCString(value));
19660 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19661 partition, parent);
19662 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19663 partition, parent);
19667 * If there is a column list, transform it to a list of column names.
19668 * Note we don't need to map this list in any way ...
19670 if (trigForm->tgattr.dim1 > 0)
19672 int i;
19674 for (i = 0; i < trigForm->tgattr.dim1; i++)
19676 Form_pg_attribute col;
19678 col = TupleDescAttr(parent->rd_att,
19679 trigForm->tgattr.values[i] - 1);
19680 cols = lappend(cols,
19681 makeString(pstrdup(NameStr(col->attname))));
19685 /* Reconstruct trigger arguments list. */
19686 if (trigForm->tgnargs > 0)
19688 char *p;
19690 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19691 RelationGetDescr(pg_trigger), &isnull);
19692 if (isnull)
19693 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19694 NameStr(trigForm->tgname), RelationGetRelationName(partition));
19696 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19698 for (int i = 0; i < trigForm->tgnargs; i++)
19700 trigargs = lappend(trigargs, makeString(pstrdup(p)));
19701 p += strlen(p) + 1;
19705 trigStmt = makeNode(CreateTrigStmt);
19706 trigStmt->replace = false;
19707 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19708 trigStmt->trigname = NameStr(trigForm->tgname);
19709 trigStmt->relation = NULL;
19710 trigStmt->funcname = NULL; /* passed separately */
19711 trigStmt->args = trigargs;
19712 trigStmt->row = true;
19713 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19714 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19715 trigStmt->columns = cols;
19716 trigStmt->whenClause = NULL; /* passed separately */
19717 trigStmt->transitionRels = NIL; /* not supported at present */
19718 trigStmt->deferrable = trigForm->tgdeferrable;
19719 trigStmt->initdeferred = trigForm->tginitdeferred;
19720 trigStmt->constrrel = NULL; /* passed separately */
19722 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19723 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19724 trigForm->tgfoid, trigForm->oid, qual,
19725 false, true, trigForm->tgenabled);
19727 MemoryContextSwitchTo(oldcxt);
19728 MemoryContextReset(perTupCxt);
19731 MemoryContextDelete(perTupCxt);
19733 systable_endscan(scan);
19734 table_close(pg_trigger, RowExclusiveLock);
19738 * ALTER TABLE DETACH PARTITION
19740 * Return the address of the relation that is no longer a partition of rel.
19742 * If concurrent mode is requested, we run in two transactions. A side-
19743 * effect is that this command cannot run in a multi-part ALTER TABLE.
19744 * Currently, that's enforced by the grammar.
19746 * The strategy for concurrency is to first modify the partition's
19747 * pg_inherit catalog row to make it visible to everyone that the
19748 * partition is detached, lock the partition against writes, and commit
19749 * the transaction; anyone who requests the partition descriptor from
19750 * that point onwards has to ignore such a partition. In a second
19751 * transaction, we wait until all transactions that could have seen the
19752 * partition as attached are gone, then we remove the rest of partition
19753 * metadata (pg_inherits and pg_class.relpartbounds).
19755 static ObjectAddress
19756 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19757 RangeVar *name, bool concurrent)
19759 Relation partRel;
19760 ObjectAddress address;
19761 Oid defaultPartOid;
19764 * We must lock the default partition, because detaching this partition
19765 * will change its partition constraint.
19767 defaultPartOid =
19768 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19769 if (OidIsValid(defaultPartOid))
19772 * Concurrent detaching when a default partition exists is not
19773 * supported. The main problem is that the default partition
19774 * constraint would change. And there's a definitional problem: what
19775 * should happen to the tuples that are being inserted that belong to
19776 * the partition being detached? Putting them on the partition being
19777 * detached would be wrong, since they'd become "lost" after the
19778 * detaching completes but we cannot put them in the default partition
19779 * either until we alter its partition constraint.
19781 * I think we could solve this problem if we effected the constraint
19782 * change before committing the first transaction. But the lock would
19783 * have to remain AEL and it would cause concurrent query planning to
19784 * be blocked, so changing it that way would be even worse.
19786 if (concurrent)
19787 ereport(ERROR,
19788 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19789 errmsg("cannot detach partitions concurrently when a default partition exists")));
19790 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19794 * In concurrent mode, the partition is locked with share-update-exclusive
19795 * in the first transaction. This allows concurrent transactions to be
19796 * doing DML to the partition.
19798 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19799 AccessExclusiveLock);
19802 * Check inheritance conditions and either delete the pg_inherits row (in
19803 * non-concurrent mode) or just set the inhdetachpending flag.
19805 if (!concurrent)
19806 RemoveInheritance(partRel, rel, false);
19807 else
19808 MarkInheritDetached(partRel, rel);
19811 * Ensure that foreign keys still hold after this detach. This keeps
19812 * locks on the referencing tables, which prevents concurrent transactions
19813 * from adding rows that we wouldn't see. For this to work in concurrent
19814 * mode, it is critical that the partition appears as no longer attached
19815 * for the RI queries as soon as the first transaction commits.
19817 ATDetachCheckNoForeignKeyRefs(partRel);
19820 * Concurrent mode has to work harder; first we add a new constraint to
19821 * the partition that matches the partition constraint. Then we close our
19822 * existing transaction, and in a new one wait for all processes to catch
19823 * up on the catalog updates we've done so far; at that point we can
19824 * complete the operation.
19826 if (concurrent)
19828 Oid partrelid,
19829 parentrelid;
19830 LOCKTAG tag;
19831 char *parentrelname;
19832 char *partrelname;
19835 * Add a new constraint to the partition being detached, which
19836 * supplants the partition constraint (unless there is one already).
19838 DetachAddConstraintIfNeeded(wqueue, partRel);
19841 * We're almost done now; the only traces that remain are the
19842 * pg_inherits tuple and the partition's relpartbounds. Before we can
19843 * remove those, we need to wait until all transactions that know that
19844 * this is a partition are gone.
19848 * Remember relation OIDs to re-acquire them later; and relation names
19849 * too, for error messages if something is dropped in between.
19851 partrelid = RelationGetRelid(partRel);
19852 parentrelid = RelationGetRelid(rel);
19853 parentrelname = MemoryContextStrdup(PortalContext,
19854 RelationGetRelationName(rel));
19855 partrelname = MemoryContextStrdup(PortalContext,
19856 RelationGetRelationName(partRel));
19858 /* Invalidate relcache entries for the parent -- must be before close */
19859 CacheInvalidateRelcache(rel);
19861 table_close(partRel, NoLock);
19862 table_close(rel, NoLock);
19863 tab->rel = NULL;
19865 /* Make updated catalog entry visible */
19866 PopActiveSnapshot();
19867 CommitTransactionCommand();
19869 StartTransactionCommand();
19872 * Now wait. This ensures that all queries that were planned
19873 * including the partition are finished before we remove the rest of
19874 * catalog entries. We don't need or indeed want to acquire this
19875 * lock, though -- that would block later queries.
19877 * We don't need to concern ourselves with waiting for a lock on the
19878 * partition itself, since we will acquire AccessExclusiveLock below.
19880 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19881 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19884 * Now acquire locks in both relations again. Note they may have been
19885 * removed in the meantime, so care is required.
19887 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19888 partRel = try_relation_open(partrelid, AccessExclusiveLock);
19890 /* If the relations aren't there, something bad happened; bail out */
19891 if (rel == NULL)
19893 if (partRel != NULL) /* shouldn't happen */
19894 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19895 partrelname);
19896 ereport(ERROR,
19897 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19898 errmsg("partitioned table \"%s\" was removed concurrently",
19899 parentrelname)));
19901 if (partRel == NULL)
19902 ereport(ERROR,
19903 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19904 errmsg("partition \"%s\" was removed concurrently", partrelname)));
19906 tab->rel = rel;
19909 /* Do the final part of detaching */
19910 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19912 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19914 /* keep our lock until commit */
19915 table_close(partRel, NoLock);
19917 return address;
19921 * Second part of ALTER TABLE .. DETACH.
19923 * This is separate so that it can be run independently when the second
19924 * transaction of the concurrent algorithm fails (crash or abort).
19926 static void
19927 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19928 Oid defaultPartOid)
19930 Relation classRel;
19931 List *fks;
19932 ListCell *cell;
19933 List *indexes;
19934 Datum new_val[Natts_pg_class];
19935 bool new_null[Natts_pg_class],
19936 new_repl[Natts_pg_class];
19937 HeapTuple tuple,
19938 newtuple;
19939 Relation trigrel = NULL;
19941 if (concurrent)
19944 * We can remove the pg_inherits row now. (In the non-concurrent case,
19945 * this was already done).
19947 RemoveInheritance(partRel, rel, true);
19950 /* Drop any triggers that were cloned on creation/attach. */
19951 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19954 * Detach any foreign keys that are inherited. This includes creating
19955 * additional action triggers.
19957 fks = copyObject(RelationGetFKeyList(partRel));
19958 if (fks != NIL)
19959 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19960 foreach(cell, fks)
19962 ForeignKeyCacheInfo *fk = lfirst(cell);
19963 HeapTuple contup;
19964 Form_pg_constraint conform;
19965 Constraint *fkconstraint;
19966 Oid insertTriggerOid,
19967 updateTriggerOid;
19969 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19970 if (!HeapTupleIsValid(contup))
19971 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19972 conform = (Form_pg_constraint) GETSTRUCT(contup);
19974 /* consider only the inherited foreign keys */
19975 if (conform->contype != CONSTRAINT_FOREIGN ||
19976 !OidIsValid(conform->conparentid))
19978 ReleaseSysCache(contup);
19979 continue;
19982 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19983 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19986 * Also, look up the partition's "check" triggers corresponding to the
19987 * constraint being detached and detach them from the parent triggers.
19989 GetForeignKeyCheckTriggers(trigrel,
19990 fk->conoid, fk->confrelid, fk->conrelid,
19991 &insertTriggerOid, &updateTriggerOid);
19992 Assert(OidIsValid(insertTriggerOid));
19993 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19994 RelationGetRelid(partRel));
19995 Assert(OidIsValid(updateTriggerOid));
19996 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19997 RelationGetRelid(partRel));
20000 * Make the action triggers on the referenced relation. When this was
20001 * a partition the action triggers pointed to the parent rel (they
20002 * still do), but now we need separate ones of our own.
20004 fkconstraint = makeNode(Constraint);
20005 fkconstraint->contype = CONSTRAINT_FOREIGN;
20006 fkconstraint->conname = pstrdup(NameStr(conform->conname));
20007 fkconstraint->deferrable = conform->condeferrable;
20008 fkconstraint->initdeferred = conform->condeferred;
20009 fkconstraint->location = -1;
20010 fkconstraint->pktable = NULL;
20011 fkconstraint->fk_attrs = NIL;
20012 fkconstraint->pk_attrs = NIL;
20013 fkconstraint->fk_matchtype = conform->confmatchtype;
20014 fkconstraint->fk_upd_action = conform->confupdtype;
20015 fkconstraint->fk_del_action = conform->confdeltype;
20016 fkconstraint->fk_del_set_cols = NIL;
20017 fkconstraint->old_conpfeqop = NIL;
20018 fkconstraint->old_pktable_oid = InvalidOid;
20019 fkconstraint->skip_validation = false;
20020 fkconstraint->initially_valid = true;
20022 createForeignKeyActionTriggers(partRel, conform->confrelid,
20023 fkconstraint, fk->conoid,
20024 conform->conindid,
20025 InvalidOid, InvalidOid,
20026 NULL, NULL);
20028 ReleaseSysCache(contup);
20030 list_free_deep(fks);
20031 if (trigrel)
20032 table_close(trigrel, RowExclusiveLock);
20035 * Any sub-constraints that are in the referenced-side of a larger
20036 * constraint have to be removed. This partition is no longer part of the
20037 * key space of the constraint.
20039 foreach(cell, GetParentedForeignKeyRefs(partRel))
20041 Oid constrOid = lfirst_oid(cell);
20042 ObjectAddress constraint;
20044 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20045 deleteDependencyRecordsForClass(ConstraintRelationId,
20046 constrOid,
20047 ConstraintRelationId,
20048 DEPENDENCY_INTERNAL);
20049 CommandCounterIncrement();
20051 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20052 performDeletion(&constraint, DROP_RESTRICT, 0);
20055 /* Now we can detach indexes */
20056 indexes = RelationGetIndexList(partRel);
20057 foreach(cell, indexes)
20059 Oid idxid = lfirst_oid(cell);
20060 Relation idx;
20061 Oid constrOid;
20063 if (!has_superclass(idxid))
20064 continue;
20066 Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
20067 RelationGetRelid(rel)));
20069 idx = index_open(idxid, AccessExclusiveLock);
20070 IndexSetParentIndex(idx, InvalidOid);
20072 /* If there's a constraint associated with the index, detach it too */
20073 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20074 idxid);
20075 if (OidIsValid(constrOid))
20076 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20078 index_close(idx, NoLock);
20081 /* Update pg_class tuple */
20082 classRel = table_open(RelationRelationId, RowExclusiveLock);
20083 tuple = SearchSysCacheCopy1(RELOID,
20084 ObjectIdGetDatum(RelationGetRelid(partRel)));
20085 if (!HeapTupleIsValid(tuple))
20086 elog(ERROR, "cache lookup failed for relation %u",
20087 RelationGetRelid(partRel));
20088 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20090 /* Clear relpartbound and reset relispartition */
20091 memset(new_val, 0, sizeof(new_val));
20092 memset(new_null, false, sizeof(new_null));
20093 memset(new_repl, false, sizeof(new_repl));
20094 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20095 new_null[Anum_pg_class_relpartbound - 1] = true;
20096 new_repl[Anum_pg_class_relpartbound - 1] = true;
20097 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20098 new_val, new_null, new_repl);
20100 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20101 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20102 heap_freetuple(newtuple);
20103 table_close(classRel, RowExclusiveLock);
20106 * Drop identity property from all identity columns of partition.
20108 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20110 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20112 if (!attr->attisdropped && attr->attidentity)
20113 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20114 AccessExclusiveLock, true, true);
20117 if (OidIsValid(defaultPartOid))
20120 * If the relation being detached is the default partition itself,
20121 * remove it from the parent's pg_partitioned_table entry.
20123 * If not, we must invalidate default partition's relcache entry, as
20124 * in StorePartitionBound: its partition constraint depends on every
20125 * other partition's partition constraint.
20127 if (RelationGetRelid(partRel) == defaultPartOid)
20128 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20129 else
20130 CacheInvalidateRelcacheByRelid(defaultPartOid);
20134 * Invalidate the parent's relcache so that the partition is no longer
20135 * included in its partition descriptor.
20137 CacheInvalidateRelcache(rel);
20140 * If the partition we just detached is partitioned itself, invalidate
20141 * relcache for all descendent partitions too to ensure that their
20142 * rd_partcheck expression trees are rebuilt; must lock partitions before
20143 * doing so, using the same lockmode as what partRel has been locked with
20144 * by the caller.
20146 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20148 List *children;
20150 children = find_all_inheritors(RelationGetRelid(partRel),
20151 AccessExclusiveLock, NULL);
20152 foreach(cell, children)
20154 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20160 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20162 * To use when a DETACH PARTITION command previously did not run to
20163 * completion; this completes the detaching process.
20165 static ObjectAddress
20166 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20168 Relation partRel;
20169 ObjectAddress address;
20170 Snapshot snap = GetActiveSnapshot();
20172 partRel = table_openrv(name, AccessExclusiveLock);
20175 * Wait until existing snapshots are gone. This is important if the
20176 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20177 * user could immediately run DETACH FINALIZE without actually waiting for
20178 * existing transactions. We must not complete the detach action until
20179 * all such queries are complete (otherwise we would present them with an
20180 * inconsistent view of catalogs).
20182 WaitForOlderSnapshots(snap->xmin, false);
20184 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20186 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20188 table_close(partRel, NoLock);
20190 return address;
20194 * DetachAddConstraintIfNeeded
20195 * Subroutine for ATExecDetachPartition. Create a constraint that
20196 * takes the place of the partition constraint, but avoid creating
20197 * a dupe if an constraint already exists which implies the needed
20198 * constraint.
20200 static void
20201 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20203 List *constraintExpr;
20205 constraintExpr = RelationGetPartitionQual(partRel);
20206 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20209 * Avoid adding a new constraint if the needed constraint is implied by an
20210 * existing constraint
20212 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20214 AlteredTableInfo *tab;
20215 Constraint *n;
20217 tab = ATGetQueueEntry(wqueue, partRel);
20219 /* Add constraint on partition, equivalent to the partition constraint */
20220 n = makeNode(Constraint);
20221 n->contype = CONSTR_CHECK;
20222 n->conname = NULL;
20223 n->location = -1;
20224 n->is_no_inherit = false;
20225 n->raw_expr = NULL;
20226 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20227 n->initially_valid = true;
20228 n->skip_validation = true;
20229 /* It's a re-add, since it nominally already exists */
20230 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20231 true, false, true, ShareUpdateExclusiveLock);
20236 * DropClonedTriggersFromPartition
20237 * subroutine for ATExecDetachPartition to remove any triggers that were
20238 * cloned to the partition when it was created-as-partition or attached.
20239 * This undoes what CloneRowTriggersToPartition did.
20241 static void
20242 DropClonedTriggersFromPartition(Oid partitionId)
20244 ScanKeyData skey;
20245 SysScanDesc scan;
20246 HeapTuple trigtup;
20247 Relation tgrel;
20248 ObjectAddresses *objects;
20250 objects = new_object_addresses();
20253 * Scan pg_trigger to search for all triggers on this rel.
20255 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20256 F_OIDEQ, ObjectIdGetDatum(partitionId));
20257 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20258 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20259 true, NULL, 1, &skey);
20260 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20262 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20263 ObjectAddress trig;
20265 /* Ignore triggers that weren't cloned */
20266 if (!OidIsValid(pg_trigger->tgparentid))
20267 continue;
20270 * Ignore internal triggers that are implementation objects of foreign
20271 * keys, because these will be detached when the foreign keys
20272 * themselves are.
20274 if (OidIsValid(pg_trigger->tgconstrrelid))
20275 continue;
20278 * This is ugly, but necessary: remove the dependency markings on the
20279 * trigger so that it can be removed.
20281 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20282 TriggerRelationId,
20283 DEPENDENCY_PARTITION_PRI);
20284 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20285 RelationRelationId,
20286 DEPENDENCY_PARTITION_SEC);
20288 /* remember this trigger to remove it below */
20289 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20290 add_exact_object_address(&trig, objects);
20293 /* make the dependency removal visible to the deletion below */
20294 CommandCounterIncrement();
20295 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20297 /* done */
20298 free_object_addresses(objects);
20299 systable_endscan(scan);
20300 table_close(tgrel, RowExclusiveLock);
20304 * Before acquiring lock on an index, acquire the same lock on the owning
20305 * table.
20307 struct AttachIndexCallbackState
20309 Oid partitionOid;
20310 Oid parentTblOid;
20311 bool lockedParentTbl;
20314 static void
20315 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20316 void *arg)
20318 struct AttachIndexCallbackState *state;
20319 Form_pg_class classform;
20320 HeapTuple tuple;
20322 state = (struct AttachIndexCallbackState *) arg;
20324 if (!state->lockedParentTbl)
20326 LockRelationOid(state->parentTblOid, AccessShareLock);
20327 state->lockedParentTbl = true;
20331 * If we previously locked some other heap, and the name we're looking up
20332 * no longer refers to an index on that relation, release the now-useless
20333 * lock. XXX maybe we should do *after* we verify whether the index does
20334 * not actually belong to the same relation ...
20336 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20338 UnlockRelationOid(state->partitionOid, AccessShareLock);
20339 state->partitionOid = InvalidOid;
20342 /* Didn't find a relation, so no need for locking or permission checks. */
20343 if (!OidIsValid(relOid))
20344 return;
20346 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20347 if (!HeapTupleIsValid(tuple))
20348 return; /* concurrently dropped, so nothing to do */
20349 classform = (Form_pg_class) GETSTRUCT(tuple);
20350 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20351 classform->relkind != RELKIND_INDEX)
20352 ereport(ERROR,
20353 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20354 errmsg("\"%s\" is not an index", rv->relname)));
20355 ReleaseSysCache(tuple);
20358 * Since we need only examine the heap's tupledesc, an access share lock
20359 * on it (preventing any DDL) is sufficient.
20361 state->partitionOid = IndexGetRelation(relOid, false);
20362 LockRelationOid(state->partitionOid, AccessShareLock);
20366 * ALTER INDEX i1 ATTACH PARTITION i2
20368 static ObjectAddress
20369 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20371 Relation partIdx;
20372 Relation partTbl;
20373 Relation parentTbl;
20374 ObjectAddress address;
20375 Oid partIdxId;
20376 Oid currParent;
20377 struct AttachIndexCallbackState state;
20380 * We need to obtain lock on the index 'name' to modify it, but we also
20381 * need to read its owning table's tuple descriptor -- so we need to lock
20382 * both. To avoid deadlocks, obtain lock on the table before doing so on
20383 * the index. Furthermore, we need to examine the parent table of the
20384 * partition, so lock that one too.
20386 state.partitionOid = InvalidOid;
20387 state.parentTblOid = parentIdx->rd_index->indrelid;
20388 state.lockedParentTbl = false;
20389 partIdxId =
20390 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20391 RangeVarCallbackForAttachIndex,
20392 (void *) &state);
20393 /* Not there? */
20394 if (!OidIsValid(partIdxId))
20395 ereport(ERROR,
20396 (errcode(ERRCODE_UNDEFINED_OBJECT),
20397 errmsg("index \"%s\" does not exist", name->relname)));
20399 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20400 partIdx = relation_open(partIdxId, AccessExclusiveLock);
20402 /* we already hold locks on both tables, so this is safe: */
20403 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20404 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20406 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20408 /* Silently do nothing if already in the right state */
20409 currParent = partIdx->rd_rel->relispartition ?
20410 get_partition_parent(partIdxId, false) : InvalidOid;
20411 if (currParent != RelationGetRelid(parentIdx))
20413 IndexInfo *childInfo;
20414 IndexInfo *parentInfo;
20415 AttrMap *attmap;
20416 bool found;
20417 int i;
20418 PartitionDesc partDesc;
20419 Oid constraintOid,
20420 cldConstrId = InvalidOid;
20423 * If this partition already has an index attached, refuse the
20424 * operation.
20426 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20428 if (OidIsValid(currParent))
20429 ereport(ERROR,
20430 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20431 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20432 RelationGetRelationName(partIdx),
20433 RelationGetRelationName(parentIdx)),
20434 errdetail("Index \"%s\" is already attached to another index.",
20435 RelationGetRelationName(partIdx))));
20437 /* Make sure it indexes a partition of the other index's table */
20438 partDesc = RelationGetPartitionDesc(parentTbl, true);
20439 found = false;
20440 for (i = 0; i < partDesc->nparts; i++)
20442 if (partDesc->oids[i] == state.partitionOid)
20444 found = true;
20445 break;
20448 if (!found)
20449 ereport(ERROR,
20450 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20451 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20452 RelationGetRelationName(partIdx),
20453 RelationGetRelationName(parentIdx)),
20454 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20455 RelationGetRelationName(partIdx),
20456 RelationGetRelationName(parentTbl))));
20458 /* Ensure the indexes are compatible */
20459 childInfo = BuildIndexInfo(partIdx);
20460 parentInfo = BuildIndexInfo(parentIdx);
20461 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20462 RelationGetDescr(parentTbl),
20463 false);
20464 if (!CompareIndexInfo(childInfo, parentInfo,
20465 partIdx->rd_indcollation,
20466 parentIdx->rd_indcollation,
20467 partIdx->rd_opfamily,
20468 parentIdx->rd_opfamily,
20469 attmap))
20470 ereport(ERROR,
20471 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20472 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20473 RelationGetRelationName(partIdx),
20474 RelationGetRelationName(parentIdx)),
20475 errdetail("The index definitions do not match.")));
20478 * If there is a constraint in the parent, make sure there is one in
20479 * the child too.
20481 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20482 RelationGetRelid(parentIdx));
20484 if (OidIsValid(constraintOid))
20486 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20487 partIdxId);
20488 if (!OidIsValid(cldConstrId))
20489 ereport(ERROR,
20490 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20491 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20492 RelationGetRelationName(partIdx),
20493 RelationGetRelationName(parentIdx)),
20494 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20495 RelationGetRelationName(parentIdx),
20496 RelationGetRelationName(parentTbl),
20497 RelationGetRelationName(partIdx))));
20501 * If it's a primary key, make sure the columns in the partition are
20502 * NOT NULL.
20504 if (parentIdx->rd_index->indisprimary)
20505 verifyPartitionIndexNotNull(childInfo, partTbl);
20507 /* All good -- do it */
20508 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20509 if (OidIsValid(constraintOid))
20510 ConstraintSetParentConstraint(cldConstrId, constraintOid,
20511 RelationGetRelid(partTbl));
20513 free_attrmap(attmap);
20515 validatePartitionedIndex(parentIdx, parentTbl);
20518 relation_close(parentTbl, AccessShareLock);
20519 /* keep these locks till commit */
20520 relation_close(partTbl, NoLock);
20521 relation_close(partIdx, NoLock);
20523 return address;
20527 * Verify whether the given partition already contains an index attached
20528 * to the given partitioned index. If so, raise an error.
20530 static void
20531 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20533 Oid existingIdx;
20535 existingIdx = index_get_partition(partitionTbl,
20536 RelationGetRelid(parentIdx));
20537 if (OidIsValid(existingIdx))
20538 ereport(ERROR,
20539 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20540 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20541 RelationGetRelationName(partIdx),
20542 RelationGetRelationName(parentIdx)),
20543 errdetail("Another index is already attached for partition \"%s\".",
20544 RelationGetRelationName(partitionTbl))));
20548 * Verify whether the set of attached partition indexes to a parent index on
20549 * a partitioned table is complete. If it is, mark the parent index valid.
20551 * This should be called each time a partition index is attached.
20553 static void
20554 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20556 Relation inheritsRel;
20557 SysScanDesc scan;
20558 ScanKeyData key;
20559 int tuples = 0;
20560 HeapTuple inhTup;
20561 bool updated = false;
20563 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20566 * Scan pg_inherits for this parent index. Count each valid index we find
20567 * (verifying the pg_index entry for each), and if we reach the total
20568 * amount we expect, we can mark this parent index as valid.
20570 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20571 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20572 BTEqualStrategyNumber, F_OIDEQ,
20573 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20574 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20575 NULL, 1, &key);
20576 while ((inhTup = systable_getnext(scan)) != NULL)
20578 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20579 HeapTuple indTup;
20580 Form_pg_index indexForm;
20582 indTup = SearchSysCache1(INDEXRELID,
20583 ObjectIdGetDatum(inhForm->inhrelid));
20584 if (!HeapTupleIsValid(indTup))
20585 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20586 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20587 if (indexForm->indisvalid)
20588 tuples += 1;
20589 ReleaseSysCache(indTup);
20592 /* Done with pg_inherits */
20593 systable_endscan(scan);
20594 table_close(inheritsRel, AccessShareLock);
20597 * If we found as many inherited indexes as the partitioned table has
20598 * partitions, we're good; update pg_index to set indisvalid.
20600 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20602 Relation idxRel;
20603 HeapTuple indTup;
20604 Form_pg_index indexForm;
20606 idxRel = table_open(IndexRelationId, RowExclusiveLock);
20607 indTup = SearchSysCacheCopy1(INDEXRELID,
20608 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20609 if (!HeapTupleIsValid(indTup))
20610 elog(ERROR, "cache lookup failed for index %u",
20611 RelationGetRelid(partedIdx));
20612 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20614 indexForm->indisvalid = true;
20615 updated = true;
20617 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20619 table_close(idxRel, RowExclusiveLock);
20620 heap_freetuple(indTup);
20624 * If this index is in turn a partition of a larger index, validating it
20625 * might cause the parent to become valid also. Try that.
20627 if (updated && partedIdx->rd_rel->relispartition)
20629 Oid parentIdxId,
20630 parentTblId;
20631 Relation parentIdx,
20632 parentTbl;
20634 /* make sure we see the validation we just did */
20635 CommandCounterIncrement();
20637 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20638 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20639 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20640 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20641 Assert(!parentIdx->rd_index->indisvalid);
20643 validatePartitionedIndex(parentIdx, parentTbl);
20645 relation_close(parentIdx, AccessExclusiveLock);
20646 relation_close(parentTbl, AccessExclusiveLock);
20651 * When attaching an index as a partition of a partitioned index which is a
20652 * primary key, verify that all the columns in the partition are marked NOT
20653 * NULL.
20655 static void
20656 verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20658 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20660 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20661 iinfo->ii_IndexAttrNumbers[i] - 1);
20663 if (!att->attnotnull)
20664 ereport(ERROR,
20665 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20666 errmsg("invalid primary key definition"),
20667 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20668 NameStr(att->attname),
20669 RelationGetRelationName(partition)));
20674 * Return an OID list of constraints that reference the given relation
20675 * that are marked as having a parent constraints.
20677 static List *
20678 GetParentedForeignKeyRefs(Relation partition)
20680 Relation pg_constraint;
20681 HeapTuple tuple;
20682 SysScanDesc scan;
20683 ScanKeyData key[2];
20684 List *constraints = NIL;
20687 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20688 * scan.
20690 if (RelationGetIndexList(partition) == NIL ||
20691 bms_is_empty(RelationGetIndexAttrBitmap(partition,
20692 INDEX_ATTR_BITMAP_KEY)))
20693 return NIL;
20695 /* Search for constraints referencing this table */
20696 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20697 ScanKeyInit(&key[0],
20698 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20699 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20700 ScanKeyInit(&key[1],
20701 Anum_pg_constraint_contype, BTEqualStrategyNumber,
20702 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20704 /* XXX This is a seqscan, as we don't have a usable index */
20705 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20706 while ((tuple = systable_getnext(scan)) != NULL)
20708 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20711 * We only need to process constraints that are part of larger ones.
20713 if (!OidIsValid(constrForm->conparentid))
20714 continue;
20716 constraints = lappend_oid(constraints, constrForm->oid);
20719 systable_endscan(scan);
20720 table_close(pg_constraint, AccessShareLock);
20722 return constraints;
20726 * During DETACH PARTITION, verify that any foreign keys pointing to the
20727 * partitioned table would not become invalid. An error is raised if any
20728 * referenced values exist.
20730 static void
20731 ATDetachCheckNoForeignKeyRefs(Relation partition)
20733 List *constraints;
20734 ListCell *cell;
20736 constraints = GetParentedForeignKeyRefs(partition);
20738 foreach(cell, constraints)
20740 Oid constrOid = lfirst_oid(cell);
20741 HeapTuple tuple;
20742 Form_pg_constraint constrForm;
20743 Relation rel;
20744 Trigger trig = {0};
20746 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20747 if (!HeapTupleIsValid(tuple))
20748 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20749 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20751 Assert(OidIsValid(constrForm->conparentid));
20752 Assert(constrForm->confrelid == RelationGetRelid(partition));
20754 /* prevent data changes into the referencing table until commit */
20755 rel = table_open(constrForm->conrelid, ShareLock);
20757 trig.tgoid = InvalidOid;
20758 trig.tgname = NameStr(constrForm->conname);
20759 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20760 trig.tgisinternal = true;
20761 trig.tgconstrrelid = RelationGetRelid(partition);
20762 trig.tgconstrindid = constrForm->conindid;
20763 trig.tgconstraint = constrForm->oid;
20764 trig.tgdeferrable = false;
20765 trig.tginitdeferred = false;
20766 /* we needn't fill in remaining fields */
20768 RI_PartitionRemove_Check(&trig, rel, partition);
20770 ReleaseSysCache(tuple);
20772 table_close(rel, NoLock);
20777 * resolve column compression specification to compression method.
20779 static char
20780 GetAttributeCompression(Oid atttypid, const char *compression)
20782 char cmethod;
20784 if (compression == NULL || strcmp(compression, "default") == 0)
20785 return InvalidCompressionMethod;
20788 * To specify a nondefault method, the column data type must be toastable.
20789 * Note this says nothing about whether the column's attstorage setting
20790 * permits compression; we intentionally allow attstorage and
20791 * attcompression to be independent. But with a non-toastable type,
20792 * attstorage could not be set to a value that would permit compression.
20794 * We don't actually need to enforce this, since nothing bad would happen
20795 * if attcompression were non-default; it would never be consulted. But
20796 * it seems more user-friendly to complain about a certainly-useless
20797 * attempt to set the property.
20799 if (!TypeIsToastable(atttypid))
20800 ereport(ERROR,
20801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20802 errmsg("column data type %s does not support compression",
20803 format_type_be(atttypid))));
20805 cmethod = CompressionNameToMethod(compression);
20806 if (!CompressionMethodIsValid(cmethod))
20807 ereport(ERROR,
20808 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20809 errmsg("invalid compression method \"%s\"", compression)));
20811 return cmethod;
20815 * resolve column storage specification
20817 static char
20818 GetAttributeStorage(Oid atttypid, const char *storagemode)
20820 char cstorage = 0;
20822 if (pg_strcasecmp(storagemode, "plain") == 0)
20823 cstorage = TYPSTORAGE_PLAIN;
20824 else if (pg_strcasecmp(storagemode, "external") == 0)
20825 cstorage = TYPSTORAGE_EXTERNAL;
20826 else if (pg_strcasecmp(storagemode, "extended") == 0)
20827 cstorage = TYPSTORAGE_EXTENDED;
20828 else if (pg_strcasecmp(storagemode, "main") == 0)
20829 cstorage = TYPSTORAGE_MAIN;
20830 else if (pg_strcasecmp(storagemode, "default") == 0)
20831 cstorage = get_typstorage(atttypid);
20832 else
20833 ereport(ERROR,
20834 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20835 errmsg("invalid storage type \"%s\"",
20836 storagemode)));
20839 * safety check: do not allow toasted storage modes unless column datatype
20840 * is TOAST-aware.
20842 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20843 ereport(ERROR,
20844 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20845 errmsg("column data type %s can only have storage PLAIN",
20846 format_type_be(atttypid))));
20848 return cstorage;