Make some error strings more generic
[pgsql.git] / src / backend / commands / tablecmds.c
blob416a98e7cef017156fbcfb07701b0d94b636ed4f
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/heapam.h"
20 #include "access/heapam_xlog.h"
21 #include "access/multixact.h"
22 #include "access/reloptions.h"
23 #include "access/relscan.h"
24 #include "access/sysattr.h"
25 #include "access/tableam.h"
26 #include "access/toast_compression.h"
27 #include "access/xact.h"
28 #include "access/xlog.h"
29 #include "access/xloginsert.h"
30 #include "catalog/catalog.h"
31 #include "catalog/heap.h"
32 #include "catalog/index.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_attrdef.h"
38 #include "catalog/pg_collation.h"
39 #include "catalog/pg_constraint.h"
40 #include "catalog/pg_depend.h"
41 #include "catalog/pg_foreign_table.h"
42 #include "catalog/pg_inherits.h"
43 #include "catalog/pg_largeobject.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_opclass.h"
46 #include "catalog/pg_statistic_ext.h"
47 #include "catalog/pg_tablespace.h"
48 #include "catalog/pg_trigger.h"
49 #include "catalog/pg_type.h"
50 #include "catalog/storage.h"
51 #include "catalog/storage_xlog.h"
52 #include "catalog/toasting.h"
53 #include "commands/cluster.h"
54 #include "commands/comment.h"
55 #include "commands/defrem.h"
56 #include "commands/event_trigger.h"
57 #include "commands/policy.h"
58 #include "commands/sequence.h"
59 #include "commands/tablecmds.h"
60 #include "commands/tablespace.h"
61 #include "commands/trigger.h"
62 #include "commands/typecmds.h"
63 #include "commands/user.h"
64 #include "commands/vacuum.h"
65 #include "executor/executor.h"
66 #include "foreign/fdwapi.h"
67 #include "foreign/foreign.h"
68 #include "miscadmin.h"
69 #include "nodes/makefuncs.h"
70 #include "nodes/nodeFuncs.h"
71 #include "nodes/parsenodes.h"
72 #include "optimizer/optimizer.h"
73 #include "parser/parse_clause.h"
74 #include "parser/parse_coerce.h"
75 #include "parser/parse_collate.h"
76 #include "parser/parse_expr.h"
77 #include "parser/parse_oper.h"
78 #include "parser/parse_relation.h"
79 #include "parser/parse_type.h"
80 #include "parser/parse_utilcmd.h"
81 #include "parser/parser.h"
82 #include "partitioning/partbounds.h"
83 #include "partitioning/partdesc.h"
84 #include "pgstat.h"
85 #include "rewrite/rewriteDefine.h"
86 #include "rewrite/rewriteHandler.h"
87 #include "rewrite/rewriteManip.h"
88 #include "storage/bufmgr.h"
89 #include "storage/lmgr.h"
90 #include "storage/lock.h"
91 #include "storage/predicate.h"
92 #include "storage/smgr.h"
93 #include "tcop/utility.h"
94 #include "utils/acl.h"
95 #include "utils/builtins.h"
96 #include "utils/fmgroids.h"
97 #include "utils/inval.h"
98 #include "utils/lsyscache.h"
99 #include "utils/memutils.h"
100 #include "utils/partcache.h"
101 #include "utils/relcache.h"
102 #include "utils/ruleutils.h"
103 #include "utils/snapmgr.h"
104 #include "utils/syscache.h"
105 #include "utils/timestamp.h"
106 #include "utils/typcache.h"
107 #include "utils/usercontext.h"
110 * ON COMMIT action list
112 typedef struct OnCommitItem
114 Oid relid; /* relid of relation */
115 OnCommitAction oncommit; /* what to do at end of xact */
118 * If this entry was created during the current transaction,
119 * creating_subid is the ID of the creating subxact; if created in a prior
120 * transaction, creating_subid is zero. If deleted during the current
121 * transaction, deleting_subid is the ID of the deleting subxact; if no
122 * deletion request is pending, deleting_subid is zero.
124 SubTransactionId creating_subid;
125 SubTransactionId deleting_subid;
126 } OnCommitItem;
128 static List *on_commits = NIL;
132 * State information for ALTER TABLE
134 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
135 * structs, one for each table modified by the operation (the named table
136 * plus any child tables that are affected). We save lists of subcommands
137 * to apply to this table (possibly modified by parse transformation steps);
138 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
139 * necessary information is stored in the constraints and newvals lists.
141 * Phase 2 is divided into multiple passes; subcommands are executed in
142 * a pass determined by subcommand type.
145 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
146 #define AT_PASS_DROP 0 /* DROP (all flavors) */
147 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
148 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
149 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
150 /* We could support a RENAME COLUMN pass here, but not currently used */
151 #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
152 #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
153 #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
154 #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
155 #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
156 #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
157 #define AT_PASS_MISC 10 /* other stuff */
158 #define AT_NUM_PASSES 11
160 typedef struct AlteredTableInfo
162 /* Information saved before any work commences: */
163 Oid relid; /* Relation to work on */
164 char relkind; /* Its relkind */
165 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
168 * Transiently set during Phase 2, normally set to NULL.
170 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
171 * returns control. This can be exploited by ATExecCmd subroutines to
172 * close/reopen across transaction boundaries.
174 Relation rel;
176 /* Information saved by Phase 1 for Phase 2: */
177 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
178 /* Information saved by Phases 1/2 for Phase 3: */
179 List *constraints; /* List of NewConstraint */
180 List *newvals; /* List of NewColumnValue */
181 List *afterStmts; /* List of utility command parsetrees */
182 bool verify_new_notnull; /* T if we should recheck NOT NULL */
183 int rewrite; /* Reason for forced rewrite, if any */
184 Oid newAccessMethod; /* new access method; 0 means no change */
185 Oid newTableSpace; /* new tablespace; 0 means no change */
186 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
187 char newrelpersistence; /* if above is true */
188 Expr *partition_constraint; /* for attach partition validation */
189 /* true, if validating default due to some other attach/detach */
190 bool validate_default;
191 /* Objects to rebuild after completing ALTER TYPE operations */
192 List *changedConstraintOids; /* OIDs of constraints to rebuild */
193 List *changedConstraintDefs; /* string definitions of same */
194 List *changedIndexOids; /* OIDs of indexes to rebuild */
195 List *changedIndexDefs; /* string definitions of same */
196 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
197 char *clusterOnIndex; /* index to use for CLUSTER */
198 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
199 List *changedStatisticsDefs; /* string definitions of same */
200 } AlteredTableInfo;
202 /* Struct describing one new constraint to check in Phase 3 scan */
203 /* Note: new not-null constraints are handled elsewhere */
204 typedef struct NewConstraint
206 char *name; /* Constraint name, or NULL if none */
207 ConstrType contype; /* CHECK or FOREIGN */
208 Oid refrelid; /* PK rel, if FOREIGN */
209 Oid refindid; /* OID of PK's index, if FOREIGN */
210 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
211 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
212 ExprState *qualstate; /* Execution state for CHECK expr */
213 } NewConstraint;
216 * Struct describing one new column value that needs to be computed during
217 * Phase 3 copy (this could be either a new column with a non-null default, or
218 * a column that we're changing the type of). Columns without such an entry
219 * are just copied from the old table during ATRewriteTable. Note that the
220 * expr is an expression over *old* table values, except when is_generated
221 * is true; then it is an expression over columns of the *new* tuple.
223 typedef struct NewColumnValue
225 AttrNumber attnum; /* which column */
226 Expr *expr; /* expression to compute */
227 ExprState *exprstate; /* execution state */
228 bool is_generated; /* is it a GENERATED expression? */
229 } NewColumnValue;
232 * Error-reporting support for RemoveRelations
234 struct dropmsgstrings
236 char kind;
237 int nonexistent_code;
238 const char *nonexistent_msg;
239 const char *skipping_msg;
240 const char *nota_msg;
241 const char *drophint_msg;
244 static const struct dropmsgstrings dropmsgstringarray[] = {
245 {RELKIND_RELATION,
246 ERRCODE_UNDEFINED_TABLE,
247 gettext_noop("table \"%s\" does not exist"),
248 gettext_noop("table \"%s\" does not exist, skipping"),
249 gettext_noop("\"%s\" is not a table"),
250 gettext_noop("Use DROP TABLE to remove a table.")},
251 {RELKIND_SEQUENCE,
252 ERRCODE_UNDEFINED_TABLE,
253 gettext_noop("sequence \"%s\" does not exist"),
254 gettext_noop("sequence \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a sequence"),
256 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
257 {RELKIND_VIEW,
258 ERRCODE_UNDEFINED_TABLE,
259 gettext_noop("view \"%s\" does not exist"),
260 gettext_noop("view \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a view"),
262 gettext_noop("Use DROP VIEW to remove a view.")},
263 {RELKIND_MATVIEW,
264 ERRCODE_UNDEFINED_TABLE,
265 gettext_noop("materialized view \"%s\" does not exist"),
266 gettext_noop("materialized view \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a materialized view"),
268 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
269 {RELKIND_INDEX,
270 ERRCODE_UNDEFINED_OBJECT,
271 gettext_noop("index \"%s\" does not exist"),
272 gettext_noop("index \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not an index"),
274 gettext_noop("Use DROP INDEX to remove an index.")},
275 {RELKIND_COMPOSITE_TYPE,
276 ERRCODE_UNDEFINED_OBJECT,
277 gettext_noop("type \"%s\" does not exist"),
278 gettext_noop("type \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a type"),
280 gettext_noop("Use DROP TYPE to remove a type.")},
281 {RELKIND_FOREIGN_TABLE,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("foreign table \"%s\" does not exist"),
284 gettext_noop("foreign table \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a foreign table"),
286 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
287 {RELKIND_PARTITIONED_TABLE,
288 ERRCODE_UNDEFINED_TABLE,
289 gettext_noop("table \"%s\" does not exist"),
290 gettext_noop("table \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a table"),
292 gettext_noop("Use DROP TABLE to remove a table.")},
293 {RELKIND_PARTITIONED_INDEX,
294 ERRCODE_UNDEFINED_OBJECT,
295 gettext_noop("index \"%s\" does not exist"),
296 gettext_noop("index \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not an index"),
298 gettext_noop("Use DROP INDEX to remove an index.")},
299 {'\0', 0, NULL, NULL, NULL, NULL}
302 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
303 struct DropRelationCallbackState
305 /* These fields are set by RemoveRelations: */
306 char expected_relkind;
307 LOCKMODE heap_lockmode;
308 /* These fields are state to track which subsidiary locks are held: */
309 Oid heapOid;
310 Oid partParentOid;
311 /* These fields are passed back by RangeVarCallbackForDropRelation: */
312 char actual_relkind;
313 char actual_relpersistence;
316 /* Alter table target-type flags for ATSimplePermissions */
317 #define ATT_TABLE 0x0001
318 #define ATT_VIEW 0x0002
319 #define ATT_MATVIEW 0x0004
320 #define ATT_INDEX 0x0008
321 #define ATT_COMPOSITE_TYPE 0x0010
322 #define ATT_FOREIGN_TABLE 0x0020
323 #define ATT_PARTITIONED_INDEX 0x0040
324 #define ATT_SEQUENCE 0x0080
327 * ForeignTruncateInfo
329 * Information related to truncation of foreign tables. This is used for
330 * the elements in a hash table. It uses the server OID as lookup key,
331 * and includes a per-server list of all foreign tables involved in the
332 * truncation.
334 typedef struct ForeignTruncateInfo
336 Oid serverid;
337 List *rels;
338 } ForeignTruncateInfo;
341 * Partition tables are expected to be dropped when the parent partitioned
342 * table gets dropped. Hence for partitioning we use AUTO dependency.
343 * Otherwise, for regular inheritance use NORMAL dependency.
345 #define child_dependency_type(child_is_partition) \
346 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
348 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
349 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
350 static void truncate_check_activity(Relation rel);
351 static void RangeVarCallbackForTruncate(const RangeVar *relation,
352 Oid relId, Oid oldRelId, void *arg);
353 static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
354 bool is_partition, List **supconstr,
355 List **supnotnulls);
356 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
357 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
358 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
359 static void StoreCatalogInheritance(Oid relationId, List *supers,
360 bool child_is_partition);
361 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
362 int32 seqNumber, Relation inhRelation,
363 bool child_is_partition);
364 static int findAttrByName(const char *attributeName, const List *columns);
365 static void AlterIndexNamespaces(Relation classRel, Relation rel,
366 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
367 static void AlterSeqNamespaces(Relation classRel, Relation rel,
368 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
369 LOCKMODE lockmode);
370 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
371 bool recurse, bool recursing, LOCKMODE lockmode);
372 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
373 Relation rel, HeapTuple contuple, List **otherrelids,
374 LOCKMODE lockmode);
375 static ObjectAddress ATExecValidateConstraint(List **wqueue,
376 Relation rel, char *constrName,
377 bool recurse, bool recursing, LOCKMODE lockmode);
378 static int transformColumnNameList(Oid relId, List *colList,
379 int16 *attnums, Oid *atttypids);
380 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
381 List **attnamelist,
382 int16 *attnums, Oid *atttypids,
383 Oid *opclasses);
384 static Oid transformFkeyCheckAttrs(Relation pkrel,
385 int numattrs, int16 *attnums,
386 Oid *opclasses);
387 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
388 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
389 Oid *funcid);
390 static void validateForeignKeyConstraint(char *conname,
391 Relation rel, Relation pkrel,
392 Oid pkindOid, Oid constraintOid);
393 static void ATController(AlterTableStmt *parsetree,
394 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
395 AlterTableUtilityContext *context);
396 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
397 bool recurse, bool recursing, LOCKMODE lockmode,
398 AlterTableUtilityContext *context);
399 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
400 AlterTableUtilityContext *context);
401 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
402 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
403 AlterTableUtilityContext *context);
404 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
405 Relation rel, AlterTableCmd *cmd,
406 bool recurse, LOCKMODE lockmode,
407 int cur_pass,
408 AlterTableUtilityContext *context);
409 static void ATRewriteTables(AlterTableStmt *parsetree,
410 List **wqueue, LOCKMODE lockmode,
411 AlterTableUtilityContext *context);
412 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
413 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
414 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
415 static void ATSimpleRecursion(List **wqueue, Relation rel,
416 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
417 AlterTableUtilityContext *context);
418 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
419 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
420 LOCKMODE lockmode,
421 AlterTableUtilityContext *context);
422 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
423 DropBehavior behavior);
424 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
425 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
426 AlterTableUtilityContext *context);
427 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
428 Relation rel, AlterTableCmd **cmd,
429 bool recurse, bool recursing,
430 LOCKMODE lockmode, int cur_pass,
431 AlterTableUtilityContext *context);
432 static bool check_for_column_name_collision(Relation rel, const char *colname,
433 bool if_not_exists);
434 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
435 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
436 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
437 LOCKMODE lockmode);
438 static bool set_attnotnull(List **wqueue, Relation rel,
439 AttrNumber attnum, bool recurse, LOCKMODE lockmode);
440 static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
441 char *constrname, char *colName,
442 bool recurse, bool recursing,
443 List **readyRels, LOCKMODE lockmode);
444 static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
445 const char *colName, LOCKMODE lockmode);
446 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
447 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
448 List *testConstraint, List *provenConstraint);
449 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
450 Node *newDefault, LOCKMODE lockmode);
451 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
452 Node *newDefault);
453 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
454 Node *def, LOCKMODE lockmode);
455 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
456 Node *def, LOCKMODE lockmode);
457 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
458 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
459 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
460 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
461 Node *newValue, LOCKMODE lockmode);
462 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
463 Node *options, bool isReset, LOCKMODE lockmode);
464 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
465 Node *newValue, LOCKMODE lockmode);
466 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
467 AlterTableCmd *cmd, LOCKMODE lockmode,
468 AlterTableUtilityContext *context);
469 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
470 DropBehavior behavior,
471 bool recurse, bool recursing,
472 bool missing_ok, LOCKMODE lockmode,
473 ObjectAddresses *addrs);
474 static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
475 LOCKMODE lockmode, AlterTableUtilityContext *context);
476 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
477 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
478 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
479 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
480 static ObjectAddress ATExecAddConstraint(List **wqueue,
481 AlteredTableInfo *tab, Relation rel,
482 Constraint *newConstraint, bool recurse, bool is_readd,
483 LOCKMODE lockmode);
484 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
485 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
486 IndexStmt *stmt, LOCKMODE lockmode);
487 static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
488 AlteredTableInfo *tab, Relation rel,
489 Constraint *constr,
490 bool recurse, bool recursing, bool is_readd,
491 LOCKMODE lockmode);
492 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
493 Relation rel, Constraint *fkconstraint,
494 bool recurse, bool recursing,
495 LOCKMODE lockmode);
496 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
497 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
498 int numfks, int16 *pkattnum, int16 *fkattnum,
499 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
500 int numfkdelsetcols, int16 *fkdelsetcols,
501 bool old_check_ok,
502 Oid parentDelTrigger, Oid parentUpdTrigger);
503 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
504 int numfksetcols, const int16 *fksetcolsattnums,
505 List *fksetcols);
506 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
507 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
508 int numfks, int16 *pkattnum, int16 *fkattnum,
509 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
510 int numfkdelsetcols, int16 *fkdelsetcols,
511 bool old_check_ok, LOCKMODE lockmode,
512 Oid parentInsTrigger, Oid parentUpdTrigger);
513 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
514 Relation partitionRel);
515 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
516 static void CloneFkReferencing(List **wqueue, Relation parentRel,
517 Relation partRel);
518 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
519 Constraint *fkconstraint, Oid constraintOid,
520 Oid indexOid,
521 Oid parentInsTrigger, Oid parentUpdTrigger,
522 Oid *insertTrigOid, Oid *updateTrigOid);
523 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
524 Constraint *fkconstraint, Oid constraintOid,
525 Oid indexOid,
526 Oid parentDelTrigger, Oid parentUpdTrigger,
527 Oid *deleteTrigOid, Oid *updateTrigOid);
528 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
529 Oid partRelid,
530 Oid parentConstrOid, int numfks,
531 AttrNumber *mapped_conkey, AttrNumber *confkey,
532 Oid *conpfeqop,
533 Oid parentInsTrigger,
534 Oid parentUpdTrigger,
535 Relation trigrel);
536 static void GetForeignKeyActionTriggers(Relation trigrel,
537 Oid conoid, Oid confrelid, Oid conrelid,
538 Oid *deleteTriggerOid,
539 Oid *updateTriggerOid);
540 static void GetForeignKeyCheckTriggers(Relation trigrel,
541 Oid conoid, Oid confrelid, Oid conrelid,
542 Oid *insertTriggerOid,
543 Oid *updateTriggerOid);
544 static void ATExecDropConstraint(Relation rel, const char *constrName,
545 DropBehavior behavior, bool recurse,
546 bool missing_ok, LOCKMODE lockmode);
547 static ObjectAddress dropconstraint_internal(Relation rel,
548 HeapTuple constraintTup, DropBehavior behavior,
549 bool recurse, bool recursing,
550 bool missing_ok, List **readyRels,
551 LOCKMODE lockmode);
552 static void ATPrepAlterColumnType(List **wqueue,
553 AlteredTableInfo *tab, Relation rel,
554 bool recurse, bool recursing,
555 AlterTableCmd *cmd, LOCKMODE lockmode,
556 AlterTableUtilityContext *context);
557 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
558 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
559 AlterTableCmd *cmd, LOCKMODE lockmode);
560 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
561 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
562 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
563 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
564 LOCKMODE lockmode);
565 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
566 char *cmd, List **wqueue, LOCKMODE lockmode,
567 bool rewrite);
568 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
569 Oid objid, Relation rel, List *domname,
570 const char *conname);
571 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
572 static void TryReuseForeignKey(Oid oldId, Constraint *con);
573 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
574 List *options, LOCKMODE lockmode);
575 static void change_owner_fix_column_acls(Oid relationOid,
576 Oid oldOwnerId, Oid newOwnerId);
577 static void change_owner_recurse_to_sequences(Oid relationOid,
578 Oid newOwnerId, LOCKMODE lockmode);
579 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
580 LOCKMODE lockmode);
581 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
582 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
583 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
584 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
585 const char *tablespacename, LOCKMODE lockmode);
586 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
587 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
588 static void ATExecSetRelOptions(Relation rel, List *defList,
589 AlterTableType operation,
590 LOCKMODE lockmode);
591 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
592 char fires_when, bool skip_system, bool recurse,
593 LOCKMODE lockmode);
594 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
595 char fires_when, LOCKMODE lockmode);
596 static void ATPrepAddInherit(Relation child_rel);
597 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
598 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
599 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
600 DependencyType deptype);
601 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
602 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
603 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
604 static void ATExecGenericOptions(Relation rel, List *options);
605 static void ATExecSetRowSecurity(Relation rel, bool rls);
606 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
607 static ObjectAddress ATExecSetCompression(Relation rel,
608 const char *column, Node *newValue, LOCKMODE lockmode);
610 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
611 static const char *storage_name(char c);
613 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
614 Oid oldRelOid, void *arg);
615 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
616 Oid oldrelid, void *arg);
617 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
618 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
619 List **partexprs, Oid *partopclass, Oid *partcollation,
620 PartitionStrategy strategy);
621 static void CreateInheritance(Relation child_rel, Relation parent_rel);
622 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
623 bool expect_detached);
624 static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
625 int inhcount);
626 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
627 PartitionCmd *cmd,
628 AlterTableUtilityContext *context);
629 static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
630 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
631 List *partConstraint,
632 bool validate_default);
633 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
634 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
635 static void DropClonedTriggersFromPartition(Oid partitionId);
636 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
637 Relation rel, RangeVar *name,
638 bool concurrent);
639 static void DetachPartitionFinalize(Relation rel, Relation partRel,
640 bool concurrent, Oid defaultPartOid);
641 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
642 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
643 RangeVar *name);
644 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
645 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
646 Relation partitionTbl);
647 static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
648 static List *GetParentedForeignKeyRefs(Relation partition);
649 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
650 static char GetAttributeCompression(Oid atttypid, const char *compression);
651 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
654 /* ----------------------------------------------------------------
655 * DefineRelation
656 * Creates a new relation.
658 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
659 * The other arguments are used to extend the behavior for other cases:
660 * relkind: relkind to assign to the new relation
661 * ownerId: if not InvalidOid, use this as the new relation's owner.
662 * typaddress: if not null, it's set to the pg_type entry's address.
663 * queryString: for error reporting
665 * Note that permissions checks are done against current user regardless of
666 * ownerId. A nonzero ownerId is used when someone is creating a relation
667 * "on behalf of" someone else, so we still want to see that the current user
668 * has permissions to do it.
670 * If successful, returns the address of the new relation.
671 * ----------------------------------------------------------------
673 ObjectAddress
674 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
675 ObjectAddress *typaddress, const char *queryString)
677 char relname[NAMEDATALEN];
678 Oid namespaceId;
679 Oid relationId;
680 Oid tablespaceId;
681 Relation rel;
682 TupleDesc descriptor;
683 List *inheritOids;
684 List *old_constraints;
685 List *old_notnulls;
686 List *rawDefaults;
687 List *cookedDefaults;
688 List *nncols;
689 Datum reloptions;
690 ListCell *listptr;
691 AttrNumber attnum;
692 bool partitioned;
693 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
694 Oid ofTypeId;
695 ObjectAddress address;
696 LOCKMODE parentLockmode;
697 const char *accessMethod = NULL;
698 Oid accessMethodId = InvalidOid;
701 * Truncate relname to appropriate length (probably a waste of time, as
702 * parser should have done this already).
704 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
707 * Check consistency of arguments
709 if (stmt->oncommit != ONCOMMIT_NOOP
710 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
711 ereport(ERROR,
712 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
713 errmsg("ON COMMIT can only be used on temporary tables")));
715 if (stmt->partspec != NULL)
717 if (relkind != RELKIND_RELATION)
718 elog(ERROR, "unexpected relkind: %d", (int) relkind);
720 relkind = RELKIND_PARTITIONED_TABLE;
721 partitioned = true;
723 else
724 partitioned = false;
727 * Look up the namespace in which we are supposed to create the relation,
728 * check we have permission to create there, lock it against concurrent
729 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
730 * namespace is selected.
732 namespaceId =
733 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
736 * Security check: disallow creating temp tables from security-restricted
737 * code. This is needed because calling code might not expect untrusted
738 * tables to appear in pg_temp at the front of its search path.
740 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
741 && InSecurityRestrictedOperation())
742 ereport(ERROR,
743 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
744 errmsg("cannot create temporary table within security-restricted operation")));
747 * Determine the lockmode to use when scanning parents. A self-exclusive
748 * lock is needed here.
750 * For regular inheritance, if two backends attempt to add children to the
751 * same parent simultaneously, and that parent has no pre-existing
752 * children, then both will attempt to update the parent's relhassubclass
753 * field, leading to a "tuple concurrently updated" error. Also, this
754 * interlocks against a concurrent ANALYZE on the parent table, which
755 * might otherwise be attempting to clear the parent's relhassubclass
756 * field, if its previous children were recently dropped.
758 * If the child table is a partition, then we instead grab an exclusive
759 * lock on the parent because its partition descriptor will be changed by
760 * addition of the new partition.
762 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
763 ShareUpdateExclusiveLock);
765 /* Determine the list of OIDs of the parents. */
766 inheritOids = NIL;
767 foreach(listptr, stmt->inhRelations)
769 RangeVar *rv = (RangeVar *) lfirst(listptr);
770 Oid parentOid;
772 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
775 * Reject duplications in the list of parents.
777 if (list_member_oid(inheritOids, parentOid))
778 ereport(ERROR,
779 (errcode(ERRCODE_DUPLICATE_TABLE),
780 errmsg("relation \"%s\" would be inherited from more than once",
781 get_rel_name(parentOid))));
783 inheritOids = lappend_oid(inheritOids, parentOid);
787 * Select tablespace to use: an explicitly indicated one, or (in the case
788 * of a partitioned table) the parent's, if it has one.
790 if (stmt->tablespacename)
792 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
794 if (partitioned && tablespaceId == MyDatabaseTableSpace)
795 ereport(ERROR,
796 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
797 errmsg("cannot specify default tablespace for partitioned relations")));
799 else if (stmt->partbound)
802 * For partitions, when no other tablespace is specified, we default
803 * the tablespace to the parent partitioned table's.
805 Assert(list_length(inheritOids) == 1);
806 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
808 else
809 tablespaceId = InvalidOid;
811 /* still nothing? use the default */
812 if (!OidIsValid(tablespaceId))
813 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
814 partitioned);
816 /* Check permissions except when using database's default */
817 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
819 AclResult aclresult;
821 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
822 ACL_CREATE);
823 if (aclresult != ACLCHECK_OK)
824 aclcheck_error(aclresult, OBJECT_TABLESPACE,
825 get_tablespace_name(tablespaceId));
828 /* In all cases disallow placing user relations in pg_global */
829 if (tablespaceId == GLOBALTABLESPACE_OID)
830 ereport(ERROR,
831 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
832 errmsg("only shared relations can be placed in pg_global tablespace")));
834 /* Identify user ID that will own the table */
835 if (!OidIsValid(ownerId))
836 ownerId = GetUserId();
839 * Parse and validate reloptions, if any.
841 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
842 true, false);
844 switch (relkind)
846 case RELKIND_VIEW:
847 (void) view_reloptions(reloptions, true);
848 break;
849 case RELKIND_PARTITIONED_TABLE:
850 (void) partitioned_table_reloptions(reloptions, true);
851 break;
852 default:
853 (void) heap_reloptions(relkind, reloptions, true);
856 if (stmt->ofTypename)
858 AclResult aclresult;
860 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
862 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
863 if (aclresult != ACLCHECK_OK)
864 aclcheck_error_type(aclresult, ofTypeId);
866 else
867 ofTypeId = InvalidOid;
870 * Look up inheritance ancestors and generate relation schema, including
871 * inherited attributes. (Note that stmt->tableElts is destructively
872 * modified by MergeAttributes.)
874 stmt->tableElts =
875 MergeAttributes(stmt->tableElts, inheritOids,
876 stmt->relation->relpersistence,
877 stmt->partbound != NULL,
878 &old_constraints, &old_notnulls);
881 * Create a tuple descriptor from the relation schema. Note that this
882 * deals with column names, types, and in-descriptor NOT NULL flags, but
883 * not default values, NOT NULL or CHECK constraints; we handle those
884 * below.
886 descriptor = BuildDescForRelation(stmt->tableElts);
889 * Find columns with default values and prepare for insertion of the
890 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
891 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
892 * while raw defaults go into a list of RawColumnDefault structs that will
893 * be processed by AddRelationNewConstraints. (We can't deal with raw
894 * expressions until we can do transformExpr.)
896 * We can set the atthasdef flags now in the tuple descriptor; this just
897 * saves StoreAttrDefault from having to do an immediate update of the
898 * pg_attribute rows.
900 rawDefaults = NIL;
901 cookedDefaults = NIL;
902 attnum = 0;
904 foreach(listptr, stmt->tableElts)
906 ColumnDef *colDef = lfirst(listptr);
907 Form_pg_attribute attr;
909 attnum++;
910 attr = TupleDescAttr(descriptor, attnum - 1);
912 if (colDef->raw_default != NULL)
914 RawColumnDefault *rawEnt;
916 Assert(colDef->cooked_default == NULL);
918 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
919 rawEnt->attnum = attnum;
920 rawEnt->raw_default = colDef->raw_default;
921 rawEnt->missingMode = false;
922 rawEnt->generated = colDef->generated;
923 rawDefaults = lappend(rawDefaults, rawEnt);
924 attr->atthasdef = true;
926 else if (colDef->cooked_default != NULL)
928 CookedConstraint *cooked;
930 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
931 cooked->contype = CONSTR_DEFAULT;
932 cooked->conoid = InvalidOid; /* until created */
933 cooked->name = NULL;
934 cooked->attnum = attnum;
935 cooked->expr = colDef->cooked_default;
936 cooked->skip_validation = false;
937 cooked->is_local = true; /* not used for defaults */
938 cooked->inhcount = 0; /* ditto */
939 cooked->is_no_inherit = false;
940 cookedDefaults = lappend(cookedDefaults, cooked);
941 attr->atthasdef = true;
946 * If the statement hasn't specified an access method, but we're defining
947 * a type of relation that needs one, use the default.
949 if (stmt->accessMethod != NULL)
951 accessMethod = stmt->accessMethod;
953 if (partitioned)
954 ereport(ERROR,
955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
956 errmsg("specifying a table access method is not supported on a partitioned table")));
958 else if (RELKIND_HAS_TABLE_AM(relkind))
959 accessMethod = default_table_access_method;
961 /* look up the access method, verify it is for a table */
962 if (accessMethod != NULL)
963 accessMethodId = get_table_am_oid(accessMethod, false);
966 * Create the relation. Inherited defaults and constraints are passed in
967 * for immediate handling --- since they don't need parsing, they can be
968 * stored immediately.
970 relationId = heap_create_with_catalog(relname,
971 namespaceId,
972 tablespaceId,
973 InvalidOid,
974 InvalidOid,
975 ofTypeId,
976 ownerId,
977 accessMethodId,
978 descriptor,
979 list_concat(cookedDefaults,
980 old_constraints),
981 relkind,
982 stmt->relation->relpersistence,
983 false,
984 false,
985 stmt->oncommit,
986 reloptions,
987 true,
988 allowSystemTableMods,
989 false,
990 InvalidOid,
991 typaddress);
994 * We must bump the command counter to make the newly-created relation
995 * tuple visible for opening.
997 CommandCounterIncrement();
1000 * Open the new relation and acquire exclusive lock on it. This isn't
1001 * really necessary for locking out other backends (since they can't see
1002 * the new rel anyway until we commit), but it keeps the lock manager from
1003 * complaining about deadlock risks.
1005 rel = relation_open(relationId, AccessExclusiveLock);
1008 * Now add any newly specified column default and generation expressions
1009 * to the new relation. These are passed to us in the form of raw
1010 * parsetrees; we need to transform them to executable expression trees
1011 * before they can be added. The most convenient way to do that is to
1012 * apply the parser's transformExpr routine, but transformExpr doesn't
1013 * work unless we have a pre-existing relation. So, the transformation has
1014 * to be postponed to this final step of CREATE TABLE.
1016 * This needs to be before processing the partitioning clauses because
1017 * those could refer to generated columns.
1019 if (rawDefaults)
1020 AddRelationNewConstraints(rel, rawDefaults, NIL,
1021 true, true, false, queryString);
1024 * Make column generation expressions visible for use by partitioning.
1026 CommandCounterIncrement();
1028 /* Process and store partition bound, if any. */
1029 if (stmt->partbound)
1031 PartitionBoundSpec *bound;
1032 ParseState *pstate;
1033 Oid parentId = linitial_oid(inheritOids),
1034 defaultPartOid;
1035 Relation parent,
1036 defaultRel = NULL;
1037 ParseNamespaceItem *nsitem;
1039 /* Already have strong enough lock on the parent */
1040 parent = table_open(parentId, NoLock);
1043 * We are going to try to validate the partition bound specification
1044 * against the partition key of parentRel, so it better have one.
1046 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1047 ereport(ERROR,
1048 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1049 errmsg("\"%s\" is not partitioned",
1050 RelationGetRelationName(parent))));
1053 * The partition constraint of the default partition depends on the
1054 * partition bounds of every other partition. It is possible that
1055 * another backend might be about to execute a query on the default
1056 * partition table, and that the query relies on previously cached
1057 * default partition constraints. We must therefore take a table lock
1058 * strong enough to prevent all queries on the default partition from
1059 * proceeding until we commit and send out a shared-cache-inval notice
1060 * that will make them update their index lists.
1062 * Order of locking: The relation being added won't be visible to
1063 * other backends until it is committed, hence here in
1064 * DefineRelation() the order of locking the default partition and the
1065 * relation being added does not matter. But at all other places we
1066 * need to lock the default relation before we lock the relation being
1067 * added or removed i.e. we should take the lock in same order at all
1068 * the places such that lock parent, lock default partition and then
1069 * lock the partition so as to avoid a deadlock.
1071 defaultPartOid =
1072 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1073 true));
1074 if (OidIsValid(defaultPartOid))
1075 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1077 /* Transform the bound values */
1078 pstate = make_parsestate(NULL);
1079 pstate->p_sourcetext = queryString;
1082 * Add an nsitem containing this relation, so that transformExpr
1083 * called on partition bound expressions is able to report errors
1084 * using a proper context.
1086 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1087 NULL, false, false);
1088 addNSItemToQuery(pstate, nsitem, false, true, true);
1090 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1093 * Check first that the new partition's bound is valid and does not
1094 * overlap with any of existing partitions of the parent.
1096 check_new_partition_bound(relname, parent, bound, pstate);
1099 * If the default partition exists, its partition constraints will
1100 * change after the addition of this new partition such that it won't
1101 * allow any row that qualifies for this new partition. So, check that
1102 * the existing data in the default partition satisfies the constraint
1103 * as it will exist after adding this partition.
1105 if (OidIsValid(defaultPartOid))
1107 check_default_partition_contents(parent, defaultRel, bound);
1108 /* Keep the lock until commit. */
1109 table_close(defaultRel, NoLock);
1112 /* Update the pg_class entry. */
1113 StorePartitionBound(rel, parent, bound);
1115 table_close(parent, NoLock);
1118 /* Store inheritance information for new rel. */
1119 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1122 * Process the partitioning specification (if any) and store the partition
1123 * key information into the catalog.
1125 if (partitioned)
1127 ParseState *pstate;
1128 int partnatts;
1129 AttrNumber partattrs[PARTITION_MAX_KEYS];
1130 Oid partopclass[PARTITION_MAX_KEYS];
1131 Oid partcollation[PARTITION_MAX_KEYS];
1132 List *partexprs = NIL;
1134 pstate = make_parsestate(NULL);
1135 pstate->p_sourcetext = queryString;
1137 partnatts = list_length(stmt->partspec->partParams);
1139 /* Protect fixed-size arrays here and in executor */
1140 if (partnatts > PARTITION_MAX_KEYS)
1141 ereport(ERROR,
1142 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1143 errmsg("cannot partition using more than %d columns",
1144 PARTITION_MAX_KEYS)));
1147 * We need to transform the raw parsetrees corresponding to partition
1148 * expressions into executable expression trees. Like column defaults
1149 * and CHECK constraints, we could not have done the transformation
1150 * earlier.
1152 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1154 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1155 partattrs, &partexprs, partopclass,
1156 partcollation, stmt->partspec->strategy);
1158 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1159 partexprs,
1160 partopclass, partcollation);
1162 /* make it all visible */
1163 CommandCounterIncrement();
1167 * If we're creating a partition, create now all the indexes, triggers,
1168 * FKs defined in the parent.
1170 * We can't do it earlier, because DefineIndex wants to know the partition
1171 * key which we just stored.
1173 if (stmt->partbound)
1175 Oid parentId = linitial_oid(inheritOids);
1176 Relation parent;
1177 List *idxlist;
1178 ListCell *cell;
1180 /* Already have strong enough lock on the parent */
1181 parent = table_open(parentId, NoLock);
1182 idxlist = RelationGetIndexList(parent);
1185 * For each index in the parent table, create one in the partition
1187 foreach(cell, idxlist)
1189 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1190 AttrMap *attmap;
1191 IndexStmt *idxstmt;
1192 Oid constraintOid;
1194 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1196 if (idxRel->rd_index->indisunique)
1197 ereport(ERROR,
1198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1199 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1200 RelationGetRelationName(parent)),
1201 errdetail("Table \"%s\" contains indexes that are unique.",
1202 RelationGetRelationName(parent))));
1203 else
1205 index_close(idxRel, AccessShareLock);
1206 continue;
1210 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1211 RelationGetDescr(parent),
1212 false);
1213 idxstmt =
1214 generateClonedIndexStmt(NULL, idxRel,
1215 attmap, &constraintOid);
1216 DefineIndex(RelationGetRelid(rel),
1217 idxstmt,
1218 InvalidOid,
1219 RelationGetRelid(idxRel),
1220 constraintOid,
1222 false, false, false, false, false);
1224 index_close(idxRel, AccessShareLock);
1227 list_free(idxlist);
1230 * If there are any row-level triggers, clone them to the new
1231 * partition.
1233 if (parent->trigdesc != NULL)
1234 CloneRowTriggersToPartition(parent, rel);
1237 * And foreign keys too. Note that because we're freshly creating the
1238 * table, there is no need to verify these new constraints.
1240 CloneForeignKeyConstraints(NULL, parent, rel);
1242 table_close(parent, NoLock);
1246 * Now add any newly specified CHECK constraints to the new relation. Same
1247 * as for defaults above, but these need to come after partitioning is set
1248 * up.
1250 if (stmt->constraints)
1251 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1252 true, true, false, queryString);
1255 * Finally, merge the not-null constraints that are declared directly with
1256 * those that come from parent relations (making sure to count inheritance
1257 * appropriately for each), create them, and set the attnotnull flag on
1258 * columns that don't yet have it.
1260 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1261 old_notnulls);
1262 foreach(listptr, nncols)
1263 set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1265 ObjectAddressSet(address, RelationRelationId, relationId);
1268 * Clean up. We keep lock on new relation (although it shouldn't be
1269 * visible to anyone else anyway, until commit).
1271 relation_close(rel, NoLock);
1273 return address;
1277 * BuildDescForRelation
1279 * Given a list of ColumnDef nodes, build a TupleDesc.
1281 * Note: tdtypeid will need to be filled in later on.
1283 TupleDesc
1284 BuildDescForRelation(const List *columns)
1286 int natts;
1287 AttrNumber attnum;
1288 ListCell *l;
1289 TupleDesc desc;
1290 bool has_not_null;
1291 char *attname;
1292 Oid atttypid;
1293 int32 atttypmod;
1294 Oid attcollation;
1295 int attdim;
1298 * allocate a new tuple descriptor
1300 natts = list_length(columns);
1301 desc = CreateTemplateTupleDesc(natts);
1302 has_not_null = false;
1304 attnum = 0;
1306 foreach(l, columns)
1308 ColumnDef *entry = lfirst(l);
1309 AclResult aclresult;
1310 Form_pg_attribute att;
1313 * for each entry in the list, get the name and type information from
1314 * the list and have TupleDescInitEntry fill in the attribute
1315 * information we need.
1317 attnum++;
1319 attname = entry->colname;
1320 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1322 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1323 if (aclresult != ACLCHECK_OK)
1324 aclcheck_error_type(aclresult, atttypid);
1326 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1327 attdim = list_length(entry->typeName->arrayBounds);
1328 if (attdim > PG_INT16_MAX)
1329 ereport(ERROR,
1330 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1331 errmsg("too many array dimensions"));
1333 if (entry->typeName->setof)
1334 ereport(ERROR,
1335 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1336 errmsg("column \"%s\" cannot be declared SETOF",
1337 attname)));
1339 TupleDescInitEntry(desc, attnum, attname,
1340 atttypid, atttypmod, attdim);
1341 att = TupleDescAttr(desc, attnum - 1);
1343 /* Override TupleDescInitEntry's settings as requested */
1344 TupleDescInitEntryCollation(desc, attnum, attcollation);
1346 /* Fill in additional stuff not handled by TupleDescInitEntry */
1347 att->attnotnull = entry->is_not_null;
1348 has_not_null |= entry->is_not_null;
1349 att->attislocal = entry->is_local;
1350 att->attinhcount = entry->inhcount;
1351 att->attidentity = entry->identity;
1352 att->attgenerated = entry->generated;
1353 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1354 if (entry->storage)
1355 att->attstorage = entry->storage;
1356 else if (entry->storage_name)
1357 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1360 if (has_not_null)
1362 TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1364 constr->has_not_null = true;
1365 constr->has_generated_stored = false;
1366 constr->defval = NULL;
1367 constr->missing = NULL;
1368 constr->num_defval = 0;
1369 constr->check = NULL;
1370 constr->num_check = 0;
1371 desc->constr = constr;
1373 else
1375 desc->constr = NULL;
1378 return desc;
1382 * Emit the right error or warning message for a "DROP" command issued on a
1383 * non-existent relation
1385 static void
1386 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1388 const struct dropmsgstrings *rentry;
1390 if (rel->schemaname != NULL &&
1391 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1393 if (!missing_ok)
1395 ereport(ERROR,
1396 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1397 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1399 else
1401 ereport(NOTICE,
1402 (errmsg("schema \"%s\" does not exist, skipping",
1403 rel->schemaname)));
1405 return;
1408 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1410 if (rentry->kind == rightkind)
1412 if (!missing_ok)
1414 ereport(ERROR,
1415 (errcode(rentry->nonexistent_code),
1416 errmsg(rentry->nonexistent_msg, rel->relname)));
1418 else
1420 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1421 break;
1426 Assert(rentry->kind != '\0'); /* Should be impossible */
1430 * Emit the right error message for a "DROP" command issued on a
1431 * relation of the wrong type
1433 static void
1434 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1436 const struct dropmsgstrings *rentry;
1437 const struct dropmsgstrings *wentry;
1439 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1440 if (rentry->kind == rightkind)
1441 break;
1442 Assert(rentry->kind != '\0');
1444 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1445 if (wentry->kind == wrongkind)
1446 break;
1447 /* wrongkind could be something we don't have in our table... */
1449 ereport(ERROR,
1450 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1451 errmsg(rentry->nota_msg, relname),
1452 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1456 * RemoveRelations
1457 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1458 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1460 void
1461 RemoveRelations(DropStmt *drop)
1463 ObjectAddresses *objects;
1464 char relkind;
1465 ListCell *cell;
1466 int flags = 0;
1467 LOCKMODE lockmode = AccessExclusiveLock;
1469 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1470 if (drop->concurrent)
1473 * Note that for temporary relations this lock may get upgraded later
1474 * on, but as no other session can access a temporary relation, this
1475 * is actually fine.
1477 lockmode = ShareUpdateExclusiveLock;
1478 Assert(drop->removeType == OBJECT_INDEX);
1479 if (list_length(drop->objects) != 1)
1480 ereport(ERROR,
1481 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1482 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1483 if (drop->behavior == DROP_CASCADE)
1484 ereport(ERROR,
1485 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1486 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1490 * First we identify all the relations, then we delete them in a single
1491 * performMultipleDeletions() call. This is to avoid unwanted DROP
1492 * RESTRICT errors if one of the relations depends on another.
1495 /* Determine required relkind */
1496 switch (drop->removeType)
1498 case OBJECT_TABLE:
1499 relkind = RELKIND_RELATION;
1500 break;
1502 case OBJECT_INDEX:
1503 relkind = RELKIND_INDEX;
1504 break;
1506 case OBJECT_SEQUENCE:
1507 relkind = RELKIND_SEQUENCE;
1508 break;
1510 case OBJECT_VIEW:
1511 relkind = RELKIND_VIEW;
1512 break;
1514 case OBJECT_MATVIEW:
1515 relkind = RELKIND_MATVIEW;
1516 break;
1518 case OBJECT_FOREIGN_TABLE:
1519 relkind = RELKIND_FOREIGN_TABLE;
1520 break;
1522 default:
1523 elog(ERROR, "unrecognized drop object type: %d",
1524 (int) drop->removeType);
1525 relkind = 0; /* keep compiler quiet */
1526 break;
1529 /* Lock and validate each relation; build a list of object addresses */
1530 objects = new_object_addresses();
1532 foreach(cell, drop->objects)
1534 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1535 Oid relOid;
1536 ObjectAddress obj;
1537 struct DropRelationCallbackState state;
1540 * These next few steps are a great deal like relation_openrv, but we
1541 * don't bother building a relcache entry since we don't need it.
1543 * Check for shared-cache-inval messages before trying to access the
1544 * relation. This is needed to cover the case where the name
1545 * identifies a rel that has been dropped and recreated since the
1546 * start of our transaction: if we don't flush the old syscache entry,
1547 * then we'll latch onto that entry and suffer an error later.
1549 AcceptInvalidationMessages();
1551 /* Look up the appropriate relation using namespace search. */
1552 state.expected_relkind = relkind;
1553 state.heap_lockmode = drop->concurrent ?
1554 ShareUpdateExclusiveLock : AccessExclusiveLock;
1555 /* We must initialize these fields to show that no locks are held: */
1556 state.heapOid = InvalidOid;
1557 state.partParentOid = InvalidOid;
1559 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1560 RangeVarCallbackForDropRelation,
1561 (void *) &state);
1563 /* Not there? */
1564 if (!OidIsValid(relOid))
1566 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1567 continue;
1571 * Decide if concurrent mode needs to be used here or not. The
1572 * callback retrieved the rel's persistence for us.
1574 if (drop->concurrent &&
1575 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1577 Assert(list_length(drop->objects) == 1 &&
1578 drop->removeType == OBJECT_INDEX);
1579 flags |= PERFORM_DELETION_CONCURRENTLY;
1583 * Concurrent index drop cannot be used with partitioned indexes,
1584 * either.
1586 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1587 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1588 ereport(ERROR,
1589 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1590 errmsg("cannot drop partitioned index \"%s\" concurrently",
1591 rel->relname)));
1594 * If we're told to drop a partitioned index, we must acquire lock on
1595 * all the children of its parent partitioned table before proceeding.
1596 * Otherwise we'd try to lock the child index partitions before their
1597 * tables, leading to potential deadlock against other sessions that
1598 * will lock those objects in the other order.
1600 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1601 (void) find_all_inheritors(state.heapOid,
1602 state.heap_lockmode,
1603 NULL);
1605 /* OK, we're ready to delete this one */
1606 obj.classId = RelationRelationId;
1607 obj.objectId = relOid;
1608 obj.objectSubId = 0;
1610 add_exact_object_address(&obj, objects);
1613 performMultipleDeletions(objects, drop->behavior, flags);
1615 free_object_addresses(objects);
1619 * Before acquiring a table lock, check whether we have sufficient rights.
1620 * In the case of DROP INDEX, also try to lock the table before the index.
1621 * Also, if the table to be dropped is a partition, we try to lock the parent
1622 * first.
1624 static void
1625 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1626 void *arg)
1628 HeapTuple tuple;
1629 struct DropRelationCallbackState *state;
1630 char expected_relkind;
1631 bool is_partition;
1632 Form_pg_class classform;
1633 LOCKMODE heap_lockmode;
1634 bool invalid_system_index = false;
1636 state = (struct DropRelationCallbackState *) arg;
1637 heap_lockmode = state->heap_lockmode;
1640 * If we previously locked some other index's heap, and the name we're
1641 * looking up no longer refers to that relation, release the now-useless
1642 * lock.
1644 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1646 UnlockRelationOid(state->heapOid, heap_lockmode);
1647 state->heapOid = InvalidOid;
1651 * Similarly, if we previously locked some other partition's heap, and the
1652 * name we're looking up no longer refers to that relation, release the
1653 * now-useless lock.
1655 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1657 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1658 state->partParentOid = InvalidOid;
1661 /* Didn't find a relation, so no need for locking or permission checks. */
1662 if (!OidIsValid(relOid))
1663 return;
1665 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1666 if (!HeapTupleIsValid(tuple))
1667 return; /* concurrently dropped, so nothing to do */
1668 classform = (Form_pg_class) GETSTRUCT(tuple);
1669 is_partition = classform->relispartition;
1671 /* Pass back some data to save lookups in RemoveRelations */
1672 state->actual_relkind = classform->relkind;
1673 state->actual_relpersistence = classform->relpersistence;
1676 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1677 * but RemoveRelations() can only pass one relkind for a given relation.
1678 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1679 * That means we must be careful before giving the wrong type error when
1680 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1681 * exists with indexes.
1683 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1684 expected_relkind = RELKIND_RELATION;
1685 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1686 expected_relkind = RELKIND_INDEX;
1687 else
1688 expected_relkind = classform->relkind;
1690 if (state->expected_relkind != expected_relkind)
1691 DropErrorMsgWrongType(rel->relname, classform->relkind,
1692 state->expected_relkind);
1694 /* Allow DROP to either table owner or schema owner */
1695 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1696 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1697 aclcheck_error(ACLCHECK_NOT_OWNER,
1698 get_relkind_objtype(classform->relkind),
1699 rel->relname);
1702 * Check the case of a system index that might have been invalidated by a
1703 * failed concurrent process and allow its drop. For the time being, this
1704 * only concerns indexes of toast relations that became invalid during a
1705 * REINDEX CONCURRENTLY process.
1707 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1709 HeapTuple locTuple;
1710 Form_pg_index indexform;
1711 bool indisvalid;
1713 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1714 if (!HeapTupleIsValid(locTuple))
1716 ReleaseSysCache(tuple);
1717 return;
1720 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1721 indisvalid = indexform->indisvalid;
1722 ReleaseSysCache(locTuple);
1724 /* Mark object as being an invalid index of system catalogs */
1725 if (!indisvalid)
1726 invalid_system_index = true;
1729 /* In the case of an invalid index, it is fine to bypass this check */
1730 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1731 ereport(ERROR,
1732 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1733 errmsg("permission denied: \"%s\" is a system catalog",
1734 rel->relname)));
1736 ReleaseSysCache(tuple);
1739 * In DROP INDEX, attempt to acquire lock on the parent table before
1740 * locking the index. index_drop() will need this anyway, and since
1741 * regular queries lock tables before their indexes, we risk deadlock if
1742 * we do it the other way around. No error if we don't find a pg_index
1743 * entry, though --- the relation may have been dropped. Note that this
1744 * code will execute for either plain or partitioned indexes.
1746 if (expected_relkind == RELKIND_INDEX &&
1747 relOid != oldRelOid)
1749 state->heapOid = IndexGetRelation(relOid, true);
1750 if (OidIsValid(state->heapOid))
1751 LockRelationOid(state->heapOid, heap_lockmode);
1755 * Similarly, if the relation is a partition, we must acquire lock on its
1756 * parent before locking the partition. That's because queries lock the
1757 * parent before its partitions, so we risk deadlock if we do it the other
1758 * way around.
1760 if (is_partition && relOid != oldRelOid)
1762 state->partParentOid = get_partition_parent(relOid, true);
1763 if (OidIsValid(state->partParentOid))
1764 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1769 * ExecuteTruncate
1770 * Executes a TRUNCATE command.
1772 * This is a multi-relation truncate. We first open and grab exclusive
1773 * lock on all relations involved, checking permissions and otherwise
1774 * verifying that the relation is OK for truncation. Note that if relations
1775 * are foreign tables, at this stage, we have not yet checked that their
1776 * foreign data in external data sources are OK for truncation. These are
1777 * checked when foreign data are actually truncated later. In CASCADE mode,
1778 * relations having FK references to the targeted relations are automatically
1779 * added to the group; in RESTRICT mode, we check that all FK references are
1780 * internal to the group that's being truncated. Finally all the relations
1781 * are truncated and reindexed.
1783 void
1784 ExecuteTruncate(TruncateStmt *stmt)
1786 List *rels = NIL;
1787 List *relids = NIL;
1788 List *relids_logged = NIL;
1789 ListCell *cell;
1792 * Open, exclusive-lock, and check all the explicitly-specified relations
1794 foreach(cell, stmt->relations)
1796 RangeVar *rv = lfirst(cell);
1797 Relation rel;
1798 bool recurse = rv->inh;
1799 Oid myrelid;
1800 LOCKMODE lockmode = AccessExclusiveLock;
1802 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1803 0, RangeVarCallbackForTruncate,
1804 NULL);
1806 /* don't throw error for "TRUNCATE foo, foo" */
1807 if (list_member_oid(relids, myrelid))
1808 continue;
1810 /* open the relation, we already hold a lock on it */
1811 rel = table_open(myrelid, NoLock);
1814 * RangeVarGetRelidExtended() has done most checks with its callback,
1815 * but other checks with the now-opened Relation remain.
1817 truncate_check_activity(rel);
1819 rels = lappend(rels, rel);
1820 relids = lappend_oid(relids, myrelid);
1822 /* Log this relation only if needed for logical decoding */
1823 if (RelationIsLogicallyLogged(rel))
1824 relids_logged = lappend_oid(relids_logged, myrelid);
1826 if (recurse)
1828 ListCell *child;
1829 List *children;
1831 children = find_all_inheritors(myrelid, lockmode, NULL);
1833 foreach(child, children)
1835 Oid childrelid = lfirst_oid(child);
1837 if (list_member_oid(relids, childrelid))
1838 continue;
1840 /* find_all_inheritors already got lock */
1841 rel = table_open(childrelid, NoLock);
1844 * It is possible that the parent table has children that are
1845 * temp tables of other backends. We cannot safely access
1846 * such tables (because of buffering issues), and the best
1847 * thing to do is to silently ignore them. Note that this
1848 * check is the same as one of the checks done in
1849 * truncate_check_activity() called below, still it is kept
1850 * here for simplicity.
1852 if (RELATION_IS_OTHER_TEMP(rel))
1854 table_close(rel, lockmode);
1855 continue;
1859 * Inherited TRUNCATE commands perform access permission
1860 * checks on the parent table only. So we skip checking the
1861 * children's permissions and don't call
1862 * truncate_check_perms() here.
1864 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1865 truncate_check_activity(rel);
1867 rels = lappend(rels, rel);
1868 relids = lappend_oid(relids, childrelid);
1870 /* Log this relation only if needed for logical decoding */
1871 if (RelationIsLogicallyLogged(rel))
1872 relids_logged = lappend_oid(relids_logged, childrelid);
1875 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1876 ereport(ERROR,
1877 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1878 errmsg("cannot truncate only a partitioned table"),
1879 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1882 ExecuteTruncateGuts(rels, relids, relids_logged,
1883 stmt->behavior, stmt->restart_seqs, false);
1885 /* And close the rels */
1886 foreach(cell, rels)
1888 Relation rel = (Relation) lfirst(cell);
1890 table_close(rel, NoLock);
1895 * ExecuteTruncateGuts
1897 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1898 * command (see above) as well as replication subscribers that execute a
1899 * replicated TRUNCATE action.
1901 * explicit_rels is the list of Relations to truncate that the command
1902 * specified. relids is the list of Oids corresponding to explicit_rels.
1903 * relids_logged is the list of Oids (a subset of relids) that require
1904 * WAL-logging. This is all a bit redundant, but the existing callers have
1905 * this information handy in this form.
1907 void
1908 ExecuteTruncateGuts(List *explicit_rels,
1909 List *relids,
1910 List *relids_logged,
1911 DropBehavior behavior, bool restart_seqs,
1912 bool run_as_table_owner)
1914 List *rels;
1915 List *seq_relids = NIL;
1916 HTAB *ft_htab = NULL;
1917 EState *estate;
1918 ResultRelInfo *resultRelInfos;
1919 ResultRelInfo *resultRelInfo;
1920 SubTransactionId mySubid;
1921 ListCell *cell;
1922 Oid *logrelids;
1925 * Check the explicitly-specified relations.
1927 * In CASCADE mode, suck in all referencing relations as well. This
1928 * requires multiple iterations to find indirectly-dependent relations. At
1929 * each phase, we need to exclusive-lock new rels before looking for their
1930 * dependencies, else we might miss something. Also, we check each rel as
1931 * soon as we open it, to avoid a faux pas such as holding lock for a long
1932 * time on a rel we have no permissions for.
1934 rels = list_copy(explicit_rels);
1935 if (behavior == DROP_CASCADE)
1937 for (;;)
1939 List *newrelids;
1941 newrelids = heap_truncate_find_FKs(relids);
1942 if (newrelids == NIL)
1943 break; /* nothing else to add */
1945 foreach(cell, newrelids)
1947 Oid relid = lfirst_oid(cell);
1948 Relation rel;
1950 rel = table_open(relid, AccessExclusiveLock);
1951 ereport(NOTICE,
1952 (errmsg("truncate cascades to table \"%s\"",
1953 RelationGetRelationName(rel))));
1954 truncate_check_rel(relid, rel->rd_rel);
1955 truncate_check_perms(relid, rel->rd_rel);
1956 truncate_check_activity(rel);
1957 rels = lappend(rels, rel);
1958 relids = lappend_oid(relids, relid);
1960 /* Log this relation only if needed for logical decoding */
1961 if (RelationIsLogicallyLogged(rel))
1962 relids_logged = lappend_oid(relids_logged, relid);
1968 * Check foreign key references. In CASCADE mode, this should be
1969 * unnecessary since we just pulled in all the references; but as a
1970 * cross-check, do it anyway if in an Assert-enabled build.
1972 #ifdef USE_ASSERT_CHECKING
1973 heap_truncate_check_FKs(rels, false);
1974 #else
1975 if (behavior == DROP_RESTRICT)
1976 heap_truncate_check_FKs(rels, false);
1977 #endif
1980 * If we are asked to restart sequences, find all the sequences, lock them
1981 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1982 * We want to do this early since it's pointless to do all the truncation
1983 * work only to fail on sequence permissions.
1985 if (restart_seqs)
1987 foreach(cell, rels)
1989 Relation rel = (Relation) lfirst(cell);
1990 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1991 ListCell *seqcell;
1993 foreach(seqcell, seqlist)
1995 Oid seq_relid = lfirst_oid(seqcell);
1996 Relation seq_rel;
1998 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2000 /* This check must match AlterSequence! */
2001 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2002 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2003 RelationGetRelationName(seq_rel));
2005 seq_relids = lappend_oid(seq_relids, seq_relid);
2007 relation_close(seq_rel, NoLock);
2012 /* Prepare to catch AFTER triggers. */
2013 AfterTriggerBeginQuery();
2016 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2017 * each relation. We don't need to call ExecOpenIndices, though.
2019 * We put the ResultRelInfos in the es_opened_result_relations list, even
2020 * though we don't have a range table and don't populate the
2021 * es_result_relations array. That's a bit bogus, but it's enough to make
2022 * ExecGetTriggerResultRel() find them.
2024 estate = CreateExecutorState();
2025 resultRelInfos = (ResultRelInfo *)
2026 palloc(list_length(rels) * sizeof(ResultRelInfo));
2027 resultRelInfo = resultRelInfos;
2028 foreach(cell, rels)
2030 Relation rel = (Relation) lfirst(cell);
2032 InitResultRelInfo(resultRelInfo,
2033 rel,
2034 0, /* dummy rangetable index */
2035 NULL,
2037 estate->es_opened_result_relations =
2038 lappend(estate->es_opened_result_relations, resultRelInfo);
2039 resultRelInfo++;
2043 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2044 * truncating (this is because one of them might throw an error). Also, if
2045 * we were to allow them to prevent statement execution, that would need
2046 * to be handled here.
2048 resultRelInfo = resultRelInfos;
2049 foreach(cell, rels)
2051 UserContext ucxt;
2053 if (run_as_table_owner)
2054 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2055 &ucxt);
2056 ExecBSTruncateTriggers(estate, resultRelInfo);
2057 if (run_as_table_owner)
2058 RestoreUserContext(&ucxt);
2059 resultRelInfo++;
2063 * OK, truncate each table.
2065 mySubid = GetCurrentSubTransactionId();
2067 foreach(cell, rels)
2069 Relation rel = (Relation) lfirst(cell);
2071 /* Skip partitioned tables as there is nothing to do */
2072 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2073 continue;
2076 * Build the lists of foreign tables belonging to each foreign server
2077 * and pass each list to the foreign data wrapper's callback function,
2078 * so that each server can truncate its all foreign tables in bulk.
2079 * Each list is saved as a single entry in a hash table that uses the
2080 * server OID as lookup key.
2082 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2084 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2085 bool found;
2086 ForeignTruncateInfo *ft_info;
2088 /* First time through, initialize hashtable for foreign tables */
2089 if (!ft_htab)
2091 HASHCTL hctl;
2093 memset(&hctl, 0, sizeof(HASHCTL));
2094 hctl.keysize = sizeof(Oid);
2095 hctl.entrysize = sizeof(ForeignTruncateInfo);
2096 hctl.hcxt = CurrentMemoryContext;
2098 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2099 32, /* start small and extend */
2100 &hctl,
2101 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2104 /* Find or create cached entry for the foreign table */
2105 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2106 if (!found)
2108 ft_info->serverid = serverid;
2109 ft_info->rels = NIL;
2113 * Save the foreign table in the entry of the server that the
2114 * foreign table belongs to.
2116 ft_info->rels = lappend(ft_info->rels, rel);
2117 continue;
2121 * Normally, we need a transaction-safe truncation here. However, if
2122 * the table was either created in the current (sub)transaction or has
2123 * a new relfilenumber in the current (sub)transaction, then we can
2124 * just truncate it in-place, because a rollback would cause the whole
2125 * table or the current physical file to be thrown away anyway.
2127 if (rel->rd_createSubid == mySubid ||
2128 rel->rd_newRelfilelocatorSubid == mySubid)
2130 /* Immediate, non-rollbackable truncation is OK */
2131 heap_truncate_one_rel(rel);
2133 else
2135 Oid heap_relid;
2136 Oid toast_relid;
2137 ReindexParams reindex_params = {0};
2140 * This effectively deletes all rows in the table, and may be done
2141 * in a serializable transaction. In that case we must record a
2142 * rw-conflict in to this transaction from each transaction
2143 * holding a predicate lock on the table.
2145 CheckTableForSerializableConflictIn(rel);
2148 * Need the full transaction-safe pushups.
2150 * Create a new empty storage file for the relation, and assign it
2151 * as the relfilenumber value. The old storage file is scheduled
2152 * for deletion at commit.
2154 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2156 heap_relid = RelationGetRelid(rel);
2159 * The same for the toast table, if any.
2161 toast_relid = rel->rd_rel->reltoastrelid;
2162 if (OidIsValid(toast_relid))
2164 Relation toastrel = relation_open(toast_relid,
2165 AccessExclusiveLock);
2167 RelationSetNewRelfilenumber(toastrel,
2168 toastrel->rd_rel->relpersistence);
2169 table_close(toastrel, NoLock);
2173 * Reconstruct the indexes to match, and we're done.
2175 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
2176 &reindex_params);
2179 pgstat_count_truncate(rel);
2182 /* Now go through the hash table, and truncate foreign tables */
2183 if (ft_htab)
2185 ForeignTruncateInfo *ft_info;
2186 HASH_SEQ_STATUS seq;
2188 hash_seq_init(&seq, ft_htab);
2190 PG_TRY();
2192 while ((ft_info = hash_seq_search(&seq)) != NULL)
2194 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2196 /* truncate_check_rel() has checked that already */
2197 Assert(routine->ExecForeignTruncate != NULL);
2199 routine->ExecForeignTruncate(ft_info->rels,
2200 behavior,
2201 restart_seqs);
2204 PG_FINALLY();
2206 hash_destroy(ft_htab);
2208 PG_END_TRY();
2212 * Restart owned sequences if we were asked to.
2214 foreach(cell, seq_relids)
2216 Oid seq_relid = lfirst_oid(cell);
2218 ResetSequence(seq_relid);
2222 * Write a WAL record to allow this set of actions to be logically
2223 * decoded.
2225 * Assemble an array of relids so we can write a single WAL record for the
2226 * whole action.
2228 if (relids_logged != NIL)
2230 xl_heap_truncate xlrec;
2231 int i = 0;
2233 /* should only get here if wal_level >= logical */
2234 Assert(XLogLogicalInfoActive());
2236 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2237 foreach(cell, relids_logged)
2238 logrelids[i++] = lfirst_oid(cell);
2240 xlrec.dbId = MyDatabaseId;
2241 xlrec.nrelids = list_length(relids_logged);
2242 xlrec.flags = 0;
2243 if (behavior == DROP_CASCADE)
2244 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2245 if (restart_seqs)
2246 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2248 XLogBeginInsert();
2249 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2250 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2252 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2254 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2258 * Process all AFTER STATEMENT TRUNCATE triggers.
2260 resultRelInfo = resultRelInfos;
2261 foreach(cell, rels)
2263 UserContext ucxt;
2265 if (run_as_table_owner)
2266 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2267 &ucxt);
2268 ExecASTruncateTriggers(estate, resultRelInfo);
2269 if (run_as_table_owner)
2270 RestoreUserContext(&ucxt);
2271 resultRelInfo++;
2274 /* Handle queued AFTER triggers */
2275 AfterTriggerEndQuery(estate);
2277 /* We can clean up the EState now */
2278 FreeExecutorState(estate);
2281 * Close any rels opened by CASCADE (can't do this while EState still
2282 * holds refs)
2284 rels = list_difference_ptr(rels, explicit_rels);
2285 foreach(cell, rels)
2287 Relation rel = (Relation) lfirst(cell);
2289 table_close(rel, NoLock);
2294 * Check that a given relation is safe to truncate. Subroutine for
2295 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2297 static void
2298 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2300 char *relname = NameStr(reltuple->relname);
2303 * Only allow truncate on regular tables, foreign tables using foreign
2304 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2305 * latter are only being included here for the following checks; no
2306 * physical truncation will occur in their case.).
2308 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2310 Oid serverid = GetForeignServerIdByRelId(relid);
2311 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2313 if (!fdwroutine->ExecForeignTruncate)
2314 ereport(ERROR,
2315 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2316 errmsg("cannot truncate foreign table \"%s\"",
2317 relname)));
2319 else if (reltuple->relkind != RELKIND_RELATION &&
2320 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2321 ereport(ERROR,
2322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2323 errmsg("\"%s\" is not a table", relname)));
2326 * Most system catalogs can't be truncated at all, or at least not unless
2327 * allow_system_table_mods=on. As an exception, however, we allow
2328 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2329 * to change its relfilenode to match the old cluster, and allowing a
2330 * TRUNCATE command to be executed is the easiest way of doing that.
2332 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2333 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2334 ereport(ERROR,
2335 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2336 errmsg("permission denied: \"%s\" is a system catalog",
2337 relname)));
2339 InvokeObjectTruncateHook(relid);
2343 * Check that current user has the permission to truncate given relation.
2345 static void
2346 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2348 char *relname = NameStr(reltuple->relname);
2349 AclResult aclresult;
2351 /* Permissions checks */
2352 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2353 if (aclresult != ACLCHECK_OK)
2354 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2355 relname);
2359 * Set of extra sanity checks to check if a given relation is safe to
2360 * truncate. This is split with truncate_check_rel() as
2361 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2363 static void
2364 truncate_check_activity(Relation rel)
2367 * Don't allow truncate on temp tables of other backends ... their local
2368 * buffer manager is not going to cope.
2370 if (RELATION_IS_OTHER_TEMP(rel))
2371 ereport(ERROR,
2372 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2373 errmsg("cannot truncate temporary tables of other sessions")));
2376 * Also check for active uses of the relation in the current transaction,
2377 * including open scans and pending AFTER trigger events.
2379 CheckTableNotInUse(rel, "TRUNCATE");
2383 * storage_name
2384 * returns the name corresponding to a typstorage/attstorage enum value
2386 static const char *
2387 storage_name(char c)
2389 switch (c)
2391 case TYPSTORAGE_PLAIN:
2392 return "PLAIN";
2393 case TYPSTORAGE_EXTERNAL:
2394 return "EXTERNAL";
2395 case TYPSTORAGE_EXTENDED:
2396 return "EXTENDED";
2397 case TYPSTORAGE_MAIN:
2398 return "MAIN";
2399 default:
2400 return "???";
2404 /*----------
2405 * MergeAttributes
2406 * Returns new schema given initial schema and superclasses.
2408 * Input arguments:
2409 * 'columns' is the column/attribute definition for the table. (It's a list
2410 * of ColumnDef's.) It is destructively changed.
2411 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2412 * 'relpersistence' is the persistence type of the table.
2413 * 'is_partition' tells if the table is a partition.
2415 * Output arguments:
2416 * 'supconstr' receives a list of constraints belonging to the parents,
2417 * updated as necessary to be valid for the child.
2418 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2419 * constraints coming from inheritance parents.
2421 * Return value:
2422 * Completed schema list.
2424 * Notes:
2425 * The order in which the attributes are inherited is very important.
2426 * Intuitively, the inherited attributes should come first. If a table
2427 * inherits from multiple parents, the order of those attributes are
2428 * according to the order of the parents specified in CREATE TABLE.
2430 * Here's an example:
2432 * create table person (name text, age int4, location point);
2433 * create table emp (salary int4, manager text) inherits(person);
2434 * create table student (gpa float8) inherits (person);
2435 * create table stud_emp (percent int4) inherits (emp, student);
2437 * The order of the attributes of stud_emp is:
2439 * person {1:name, 2:age, 3:location}
2440 * / \
2441 * {6:gpa} student emp {4:salary, 5:manager}
2442 * \ /
2443 * stud_emp {7:percent}
2445 * If the same attribute name appears multiple times, then it appears
2446 * in the result table in the proper location for its first appearance.
2448 * Constraints (including not-null constraints) for the child table
2449 * are the union of all relevant constraints, from both the child schema
2450 * and parent tables. In addition, in legacy inheritance, each column that
2451 * appears in a primary key in any of the parents also gets a NOT NULL
2452 * constraint (partitioning doesn't need this, because the PK itself gets
2453 * inherited.)
2455 * The default value for a child column is defined as:
2456 * (1) If the child schema specifies a default, that value is used.
2457 * (2) If neither the child nor any parent specifies a default, then
2458 * the column will not have a default.
2459 * (3) If conflicting defaults are inherited from different parents
2460 * (and not overridden by the child), an error is raised.
2461 * (4) Otherwise the inherited default is used.
2463 * Note that the default-value infrastructure is used for generated
2464 * columns' expressions too, so most of the preceding paragraph applies
2465 * to generation expressions too. We insist that a child column be
2466 * generated if and only if its parent(s) are, but it need not have
2467 * the same generation expression.
2468 *----------
2470 static List *
2471 MergeAttributes(List *columns, const List *supers, char relpersistence,
2472 bool is_partition, List **supconstr, List **supnotnulls)
2474 List *inh_columns = NIL;
2475 List *constraints = NIL;
2476 List *nnconstraints = NIL;
2477 bool have_bogus_defaults = false;
2478 int child_attno;
2479 static Node bogus_marker = {0}; /* marks conflicting defaults */
2480 List *saved_columns = NIL;
2481 ListCell *lc;
2484 * Check for and reject tables with too many columns. We perform this
2485 * check relatively early for two reasons: (a) we don't run the risk of
2486 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2487 * okay if we're processing <= 1600 columns, but could take minutes to
2488 * execute if the user attempts to create a table with hundreds of
2489 * thousands of columns.
2491 * Note that we also need to check that we do not exceed this figure after
2492 * including columns from inherited relations.
2494 if (list_length(columns) > MaxHeapAttributeNumber)
2495 ereport(ERROR,
2496 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2497 errmsg("tables can have at most %d columns",
2498 MaxHeapAttributeNumber)));
2501 * Check for duplicate names in the explicit list of attributes.
2503 * Although we might consider merging such entries in the same way that we
2504 * handle name conflicts for inherited attributes, it seems to make more
2505 * sense to assume such conflicts are errors.
2507 * We don't use foreach() here because we have two nested loops over the
2508 * columns list, with possible element deletions in the inner one. If we
2509 * used foreach_delete_current() it could only fix up the state of one of
2510 * the loops, so it seems cleaner to use looping over list indexes for
2511 * both loops. Note that any deletion will happen beyond where the outer
2512 * loop is, so its index never needs adjustment.
2514 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2516 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2518 if (!is_partition && coldef->typeName == NULL)
2521 * Typed table column option that does not belong to a column from
2522 * the type. This works because the columns from the type come
2523 * first in the list. (We omit this check for partition column
2524 * lists; those are processed separately below.)
2526 ereport(ERROR,
2527 (errcode(ERRCODE_UNDEFINED_COLUMN),
2528 errmsg("column \"%s\" does not exist",
2529 coldef->colname)));
2532 /* restpos scans all entries beyond coldef; incr is in loop body */
2533 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2535 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2537 if (strcmp(coldef->colname, restdef->colname) == 0)
2539 if (coldef->is_from_type)
2542 * merge the column options into the column from the type
2544 coldef->is_not_null = restdef->is_not_null;
2545 coldef->raw_default = restdef->raw_default;
2546 coldef->cooked_default = restdef->cooked_default;
2547 coldef->constraints = restdef->constraints;
2548 coldef->is_from_type = false;
2549 columns = list_delete_nth_cell(columns, restpos);
2551 else
2552 ereport(ERROR,
2553 (errcode(ERRCODE_DUPLICATE_COLUMN),
2554 errmsg("column \"%s\" specified more than once",
2555 coldef->colname)));
2557 else
2558 restpos++;
2563 * In case of a partition, there are no new column definitions, only dummy
2564 * ColumnDefs created for column constraints. Set them aside for now and
2565 * process them at the end.
2567 if (is_partition)
2569 saved_columns = columns;
2570 columns = NIL;
2574 * Scan the parents left-to-right, and merge their attributes to form a
2575 * list of inherited columns (inh_columns).
2577 child_attno = 0;
2578 foreach(lc, supers)
2580 Oid parent = lfirst_oid(lc);
2581 Relation relation;
2582 TupleDesc tupleDesc;
2583 TupleConstr *constr;
2584 AttrMap *newattmap;
2585 List *inherited_defaults;
2586 List *cols_with_defaults;
2587 List *nnconstrs;
2588 ListCell *lc1;
2589 ListCell *lc2;
2590 Bitmapset *pkattrs;
2591 Bitmapset *nncols = NULL;
2593 /* caller already got lock */
2594 relation = table_open(parent, NoLock);
2597 * Check for active uses of the parent partitioned table in the
2598 * current transaction, such as being used in some manner by an
2599 * enclosing command.
2601 if (is_partition)
2602 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2605 * We do not allow partitioned tables and partitions to participate in
2606 * regular inheritance.
2608 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2609 ereport(ERROR,
2610 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2611 errmsg("cannot inherit from partitioned table \"%s\"",
2612 RelationGetRelationName(relation))));
2613 if (relation->rd_rel->relispartition && !is_partition)
2614 ereport(ERROR,
2615 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2616 errmsg("cannot inherit from partition \"%s\"",
2617 RelationGetRelationName(relation))));
2619 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2620 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2621 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2622 ereport(ERROR,
2623 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2624 errmsg("inherited relation \"%s\" is not a table or foreign table",
2625 RelationGetRelationName(relation))));
2628 * If the parent is permanent, so must be all of its partitions. Note
2629 * that inheritance allows that case.
2631 if (is_partition &&
2632 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2633 relpersistence == RELPERSISTENCE_TEMP)
2634 ereport(ERROR,
2635 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2636 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2637 RelationGetRelationName(relation))));
2639 /* Permanent rels cannot inherit from temporary ones */
2640 if (relpersistence != RELPERSISTENCE_TEMP &&
2641 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2642 ereport(ERROR,
2643 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2644 errmsg(!is_partition
2645 ? "cannot inherit from temporary relation \"%s\""
2646 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2647 RelationGetRelationName(relation))));
2649 /* If existing rel is temp, it must belong to this session */
2650 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2651 !relation->rd_islocaltemp)
2652 ereport(ERROR,
2653 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2654 errmsg(!is_partition
2655 ? "cannot inherit from temporary relation of another session"
2656 : "cannot create as partition of temporary relation of another session")));
2659 * We should have an UNDER permission flag for this, but for now,
2660 * demand that creator of a child table own the parent.
2662 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2663 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2664 RelationGetRelationName(relation));
2666 tupleDesc = RelationGetDescr(relation);
2667 constr = tupleDesc->constr;
2670 * newattmap->attnums[] will contain the child-table attribute numbers
2671 * for the attributes of this parent table. (They are not the same
2672 * for parents after the first one, nor if we have dropped columns.)
2674 newattmap = make_attrmap(tupleDesc->natts);
2676 /* We can't process inherited defaults until newattmap is complete. */
2677 inherited_defaults = cols_with_defaults = NIL;
2680 * All columns that are part of the parent's primary key need to be
2681 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2682 * constraint (if they don't have one already). Also, we request
2683 * attnotnull on columns that have a not-null constraint that's not
2684 * marked NO INHERIT.
2686 pkattrs = RelationGetIndexAttrBitmap(relation,
2687 INDEX_ATTR_BITMAP_PRIMARY_KEY);
2688 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
2689 foreach(lc1, nnconstrs)
2690 nncols = bms_add_member(nncols,
2691 ((CookedConstraint *) lfirst(lc1))->attnum);
2693 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2694 parent_attno++)
2696 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2697 parent_attno - 1);
2698 char *attributeName = NameStr(attribute->attname);
2699 int exist_attno;
2700 ColumnDef *def;
2703 * Ignore dropped columns in the parent.
2705 if (attribute->attisdropped)
2706 continue; /* leave newattmap->attnums entry as zero */
2709 * Does it conflict with some previously inherited column?
2711 exist_attno = findAttrByName(attributeName, inh_columns);
2712 if (exist_attno > 0)
2714 Oid defTypeId;
2715 int32 deftypmod;
2716 Oid defCollId;
2719 * Yes, try to merge the two column definitions.
2721 ereport(NOTICE,
2722 (errmsg("merging multiple inherited definitions of column \"%s\"",
2723 attributeName)));
2724 def = (ColumnDef *) list_nth(inh_columns, exist_attno - 1);
2727 * Must have the same type and typmod
2729 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2730 if (defTypeId != attribute->atttypid ||
2731 deftypmod != attribute->atttypmod)
2732 ereport(ERROR,
2733 (errcode(ERRCODE_DATATYPE_MISMATCH),
2734 errmsg("inherited column \"%s\" has a type conflict",
2735 attributeName),
2736 errdetail("%s versus %s",
2737 format_type_with_typemod(defTypeId,
2738 deftypmod),
2739 format_type_with_typemod(attribute->atttypid,
2740 attribute->atttypmod))));
2743 * Must have the same collation
2745 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
2746 if (defCollId != attribute->attcollation)
2747 ereport(ERROR,
2748 (errcode(ERRCODE_COLLATION_MISMATCH),
2749 errmsg("inherited column \"%s\" has a collation conflict",
2750 attributeName),
2751 errdetail("\"%s\" versus \"%s\"",
2752 get_collation_name(defCollId),
2753 get_collation_name(attribute->attcollation))));
2756 * Copy/check storage parameter
2758 if (def->storage == 0)
2759 def->storage = attribute->attstorage;
2760 else if (def->storage != attribute->attstorage)
2761 ereport(ERROR,
2762 (errcode(ERRCODE_DATATYPE_MISMATCH),
2763 errmsg("inherited column \"%s\" has a storage parameter conflict",
2764 attributeName),
2765 errdetail("%s versus %s",
2766 storage_name(def->storage),
2767 storage_name(attribute->attstorage))));
2770 * Copy/check compression parameter
2772 if (CompressionMethodIsValid(attribute->attcompression))
2774 const char *compression =
2775 GetCompressionMethodName(attribute->attcompression);
2777 if (def->compression == NULL)
2778 def->compression = pstrdup(compression);
2779 else if (strcmp(def->compression, compression) != 0)
2780 ereport(ERROR,
2781 (errcode(ERRCODE_DATATYPE_MISMATCH),
2782 errmsg("column \"%s\" has a compression method conflict",
2783 attributeName),
2784 errdetail("%s versus %s", def->compression, compression)));
2788 * In regular inheritance, columns in the parent's primary key
2789 * get an extra not-null constraint. Partitioning doesn't
2790 * need this, because the PK itself is going to be cloned to
2791 * the partition.
2793 if (!is_partition &&
2794 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2795 pkattrs))
2797 CookedConstraint *nn;
2799 nn = palloc(sizeof(CookedConstraint));
2800 nn->contype = CONSTR_NOTNULL;
2801 nn->conoid = InvalidOid;
2802 nn->name = NULL;
2803 nn->attnum = exist_attno;
2804 nn->expr = NULL;
2805 nn->skip_validation = false;
2806 nn->is_local = false;
2807 nn->inhcount = 1;
2808 nn->is_no_inherit = false;
2810 nnconstraints = lappend(nnconstraints, nn);
2814 * mark attnotnull if parent has it and it's not NO INHERIT
2816 if (bms_is_member(parent_attno, nncols) ||
2817 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2818 pkattrs))
2819 def->is_not_null = true;
2822 * Check for GENERATED conflicts
2824 if (def->generated != attribute->attgenerated)
2825 ereport(ERROR,
2826 (errcode(ERRCODE_DATATYPE_MISMATCH),
2827 errmsg("inherited column \"%s\" has a generation conflict",
2828 attributeName)));
2831 * Default and other constraints are handled below
2834 def->inhcount++;
2835 if (def->inhcount < 0)
2836 ereport(ERROR,
2837 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2838 errmsg("too many inheritance parents"));
2840 newattmap->attnums[parent_attno - 1] = exist_attno;
2842 else
2845 * No, create a new inherited column
2847 def = makeColumnDef(attributeName, attribute->atttypid,
2848 attribute->atttypmod, attribute->attcollation);
2849 def->inhcount = 1;
2850 def->is_local = false;
2851 /* mark attnotnull if parent has it and it's not NO INHERIT */
2852 if (bms_is_member(parent_attno, nncols) ||
2853 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2854 pkattrs))
2855 def->is_not_null = true;
2856 def->storage = attribute->attstorage;
2857 def->generated = attribute->attgenerated;
2858 if (CompressionMethodIsValid(attribute->attcompression))
2859 def->compression =
2860 pstrdup(GetCompressionMethodName(attribute->attcompression));
2861 inh_columns = lappend(inh_columns, def);
2862 newattmap->attnums[parent_attno - 1] = ++child_attno;
2865 * In regular inheritance, columns in the parent's primary key
2866 * get an extra not-null constraint. Partitioning doesn't
2867 * need this, because the PK itself is going to be cloned to
2868 * the partition.
2870 if (!is_partition &&
2871 bms_is_member(parent_attno -
2872 FirstLowInvalidHeapAttributeNumber,
2873 pkattrs))
2875 CookedConstraint *nn;
2877 nn = palloc(sizeof(CookedConstraint));
2878 nn->contype = CONSTR_NOTNULL;
2879 nn->conoid = InvalidOid;
2880 nn->name = NULL;
2881 nn->attnum = newattmap->attnums[parent_attno - 1];
2882 nn->expr = NULL;
2883 nn->skip_validation = false;
2884 nn->is_local = false;
2885 nn->inhcount = 1;
2886 nn->is_no_inherit = false;
2888 nnconstraints = lappend(nnconstraints, nn);
2893 * Locate default/generation expression if any
2895 if (attribute->atthasdef)
2897 Node *this_default;
2899 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2900 if (this_default == NULL)
2901 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2902 parent_attno, RelationGetRelationName(relation));
2905 * If it's a GENERATED default, it might contain Vars that
2906 * need to be mapped to the inherited column(s)' new numbers.
2907 * We can't do that till newattmap is ready, so just remember
2908 * all the inherited default expressions for the moment.
2910 inherited_defaults = lappend(inherited_defaults, this_default);
2911 cols_with_defaults = lappend(cols_with_defaults, def);
2916 * Now process any inherited default expressions, adjusting attnos
2917 * using the completed newattmap map.
2919 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2921 Node *this_default = (Node *) lfirst(lc1);
2922 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2923 bool found_whole_row;
2925 /* Adjust Vars to match new table's column numbering */
2926 this_default = map_variable_attnos(this_default,
2927 1, 0,
2928 newattmap,
2929 InvalidOid, &found_whole_row);
2932 * For the moment we have to reject whole-row variables. We could
2933 * convert them, if we knew the new table's rowtype OID, but that
2934 * hasn't been assigned yet. (A variable could only appear in a
2935 * generation expression, so the error message is correct.)
2937 if (found_whole_row)
2938 ereport(ERROR,
2939 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2940 errmsg("cannot convert whole-row table reference"),
2941 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2942 def->colname,
2943 RelationGetRelationName(relation))));
2946 * If we already had a default from some prior parent, check to
2947 * see if they are the same. If so, no problem; if not, mark the
2948 * column as having a bogus default. Below, we will complain if
2949 * the bogus default isn't overridden by the child columns.
2951 Assert(def->raw_default == NULL);
2952 if (def->cooked_default == NULL)
2953 def->cooked_default = this_default;
2954 else if (!equal(def->cooked_default, this_default))
2956 def->cooked_default = &bogus_marker;
2957 have_bogus_defaults = true;
2962 * Now copy the CHECK constraints of this parent, adjusting attnos
2963 * using the completed newattmap map. Identically named constraints
2964 * are merged if possible, else we throw error.
2966 if (constr && constr->num_check > 0)
2968 ConstrCheck *check = constr->check;
2970 for (int i = 0; i < constr->num_check; i++)
2972 char *name = check[i].ccname;
2973 Node *expr;
2974 bool found_whole_row;
2976 /* ignore if the constraint is non-inheritable */
2977 if (check[i].ccnoinherit)
2978 continue;
2980 /* Adjust Vars to match new table's column numbering */
2981 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2982 1, 0,
2983 newattmap,
2984 InvalidOid, &found_whole_row);
2987 * For the moment we have to reject whole-row variables. We
2988 * could convert them, if we knew the new table's rowtype OID,
2989 * but that hasn't been assigned yet.
2991 if (found_whole_row)
2992 ereport(ERROR,
2993 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2994 errmsg("cannot convert whole-row table reference"),
2995 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2996 name,
2997 RelationGetRelationName(relation))));
2999 constraints = MergeCheckConstraint(constraints, name, expr);
3004 * Also copy the not-null constraints from this parent. The
3005 * attnotnull markings were already installed above.
3007 foreach(lc1, nnconstrs)
3009 CookedConstraint *nn = lfirst(lc1);
3011 Assert(nn->contype == CONSTR_NOTNULL);
3013 nn->attnum = newattmap->attnums[nn->attnum - 1];
3014 nn->is_local = false;
3015 nn->inhcount = 1;
3017 nnconstraints = lappend(nnconstraints, nn);
3020 free_attrmap(newattmap);
3023 * Close the parent rel, but keep our lock on it until xact commit.
3024 * That will prevent someone else from deleting or ALTERing the parent
3025 * before the child is committed.
3027 table_close(relation, NoLock);
3031 * If we had no inherited attributes, the result columns are just the
3032 * explicitly declared columns. Otherwise, we need to merge the declared
3033 * columns into the inherited column list. Although, we never have any
3034 * explicitly declared columns if the table is a partition.
3036 if (inh_columns != NIL)
3038 int newcol_attno = 0;
3040 foreach(lc, columns)
3042 ColumnDef *newdef = lfirst(lc);
3043 char *attributeName = newdef->colname;
3044 int exist_attno;
3046 newcol_attno++;
3049 * Does it conflict with some previously inherited column?
3051 exist_attno = findAttrByName(attributeName, inh_columns);
3052 if (exist_attno > 0)
3054 ColumnDef *def;
3055 Oid defTypeId,
3056 newTypeId;
3057 int32 deftypmod,
3058 newtypmod;
3059 Oid defcollid,
3060 newcollid;
3063 * Partitions have only one parent and have no column
3064 * definitions of their own, so conflict should never occur.
3066 Assert(!is_partition);
3069 * Yes, try to merge the two column definitions.
3071 if (exist_attno == newcol_attno)
3072 ereport(NOTICE,
3073 (errmsg("merging column \"%s\" with inherited definition",
3074 attributeName)));
3075 else
3076 ereport(NOTICE,
3077 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3078 errdetail("User-specified column moved to the position of the inherited column.")));
3079 def = (ColumnDef *) list_nth(inh_columns, exist_attno - 1);
3082 * Must have the same type and typmod
3084 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
3085 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
3086 if (defTypeId != newTypeId || deftypmod != newtypmod)
3087 ereport(ERROR,
3088 (errcode(ERRCODE_DATATYPE_MISMATCH),
3089 errmsg("column \"%s\" has a type conflict",
3090 attributeName),
3091 errdetail("%s versus %s",
3092 format_type_with_typemod(defTypeId,
3093 deftypmod),
3094 format_type_with_typemod(newTypeId,
3095 newtypmod))));
3098 * Must have the same collation
3100 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
3101 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
3102 if (defcollid != newcollid)
3103 ereport(ERROR,
3104 (errcode(ERRCODE_COLLATION_MISMATCH),
3105 errmsg("column \"%s\" has a collation conflict",
3106 attributeName),
3107 errdetail("\"%s\" versus \"%s\"",
3108 get_collation_name(defcollid),
3109 get_collation_name(newcollid))));
3112 * Identity is never inherited. The new column can have an
3113 * identity definition, so we always just take that one.
3115 def->identity = newdef->identity;
3118 * Copy storage parameter
3120 if (def->storage == 0)
3121 def->storage = newdef->storage;
3122 else if (newdef->storage != 0 && def->storage != newdef->storage)
3123 ereport(ERROR,
3124 (errcode(ERRCODE_DATATYPE_MISMATCH),
3125 errmsg("column \"%s\" has a storage parameter conflict",
3126 attributeName),
3127 errdetail("%s versus %s",
3128 storage_name(def->storage),
3129 storage_name(newdef->storage))));
3132 * Copy compression parameter
3134 if (def->compression == NULL)
3135 def->compression = newdef->compression;
3136 else if (newdef->compression != NULL)
3138 if (strcmp(def->compression, newdef->compression) != 0)
3139 ereport(ERROR,
3140 (errcode(ERRCODE_DATATYPE_MISMATCH),
3141 errmsg("column \"%s\" has a compression method conflict",
3142 attributeName),
3143 errdetail("%s versus %s", def->compression, newdef->compression)));
3147 * Merge of not-null constraints = OR 'em together
3149 def->is_not_null |= newdef->is_not_null;
3152 * Check for conflicts related to generated columns.
3154 * If the parent column is generated, the child column will be
3155 * made a generated column if it isn't already. If it is a
3156 * generated column, we'll take its generation expression in
3157 * preference to the parent's. We must check that the child
3158 * column doesn't specify a default value or identity, which
3159 * matches the rules for a single column in parse_utilcmd.c.
3161 * Conversely, if the parent column is not generated, the
3162 * child column can't be either. (We used to allow that, but
3163 * it results in being able to override the generation
3164 * expression via UPDATEs through the parent.)
3166 if (def->generated)
3168 if (newdef->raw_default && !newdef->generated)
3169 ereport(ERROR,
3170 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3171 errmsg("column \"%s\" inherits from generated column but specifies default",
3172 def->colname)));
3173 if (newdef->identity)
3174 ereport(ERROR,
3175 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3176 errmsg("column \"%s\" inherits from generated column but specifies identity",
3177 def->colname)));
3179 else
3181 if (newdef->generated)
3182 ereport(ERROR,
3183 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3184 errmsg("child column \"%s\" specifies generation expression",
3185 def->colname),
3186 errhint("A child table column cannot be generated unless its parent column is.")));
3190 * If new def has a default, override previous default
3192 if (newdef->raw_default != NULL)
3194 def->raw_default = newdef->raw_default;
3195 def->cooked_default = newdef->cooked_default;
3198 /* Mark the column as locally defined */
3199 def->is_local = true;
3201 else
3204 * No, attach new column to result columns
3206 inh_columns = lappend(inh_columns, newdef);
3210 columns = inh_columns;
3213 * Check that we haven't exceeded the legal # of columns after merging
3214 * in inherited columns.
3216 if (list_length(columns) > MaxHeapAttributeNumber)
3217 ereport(ERROR,
3218 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3219 errmsg("tables can have at most %d columns",
3220 MaxHeapAttributeNumber)));
3224 * Now that we have the column definition list for a partition, we can
3225 * check whether the columns referenced in the column constraint specs
3226 * actually exist. Also, merge column defaults.
3228 if (is_partition)
3230 foreach(lc, saved_columns)
3232 ColumnDef *restdef = lfirst(lc);
3233 bool found = false;
3234 ListCell *l;
3236 foreach(l, columns)
3238 ColumnDef *coldef = lfirst(l);
3240 if (strcmp(coldef->colname, restdef->colname) == 0)
3242 found = true;
3245 * Check for conflicts related to generated columns.
3247 * Same rules as above: generated-ness has to match the
3248 * parent, but the contents of the generation expression
3249 * can be different.
3251 if (coldef->generated)
3253 if (restdef->raw_default && !restdef->generated)
3254 ereport(ERROR,
3255 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3256 errmsg("column \"%s\" inherits from generated column but specifies default",
3257 restdef->colname)));
3258 if (restdef->identity)
3259 ereport(ERROR,
3260 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3261 errmsg("column \"%s\" inherits from generated column but specifies identity",
3262 restdef->colname)));
3264 else
3266 if (restdef->generated)
3267 ereport(ERROR,
3268 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3269 errmsg("child column \"%s\" specifies generation expression",
3270 restdef->colname),
3271 errhint("A child table column cannot be generated unless its parent column is.")));
3275 * Override the parent's default value for this column
3276 * (coldef->cooked_default) with the partition's local
3277 * definition (restdef->raw_default), if there's one. It
3278 * should be physically impossible to get a cooked default
3279 * in the local definition or a raw default in the
3280 * inherited definition, but make sure they're nulls, for
3281 * future-proofing.
3283 Assert(restdef->cooked_default == NULL);
3284 Assert(coldef->raw_default == NULL);
3285 if (restdef->raw_default)
3287 coldef->raw_default = restdef->raw_default;
3288 coldef->cooked_default = NULL;
3293 /* complain for constraints on columns not in parent */
3294 if (!found)
3295 ereport(ERROR,
3296 (errcode(ERRCODE_UNDEFINED_COLUMN),
3297 errmsg("column \"%s\" does not exist",
3298 restdef->colname)));
3303 * If we found any conflicting parent default values, check to make sure
3304 * they were overridden by the child.
3306 if (have_bogus_defaults)
3308 foreach(lc, columns)
3310 ColumnDef *def = lfirst(lc);
3312 if (def->cooked_default == &bogus_marker)
3314 if (def->generated)
3315 ereport(ERROR,
3316 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3317 errmsg("column \"%s\" inherits conflicting generation expressions",
3318 def->colname),
3319 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3320 else
3321 ereport(ERROR,
3322 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3323 errmsg("column \"%s\" inherits conflicting default values",
3324 def->colname),
3325 errhint("To resolve the conflict, specify a default explicitly.")));
3330 *supconstr = constraints;
3331 *supnotnulls = nnconstraints;
3333 return columns;
3338 * MergeCheckConstraint
3339 * Try to merge an inherited CHECK constraint with previous ones
3341 * If we inherit identically-named constraints from multiple parents, we must
3342 * merge them, or throw an error if they don't have identical definitions.
3344 * constraints is a list of CookedConstraint structs for previous constraints.
3346 * If the new constraint matches an existing one, then the existing
3347 * constraint's inheritance count is updated. If there is a conflict (same
3348 * name but different expression), throw an error. If the constraint neither
3349 * matches nor conflicts with an existing one, a new constraint is appended to
3350 * the list.
3352 static List *
3353 MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3355 ListCell *lc;
3356 CookedConstraint *newcon;
3358 foreach(lc, constraints)
3360 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3362 Assert(ccon->contype == CONSTR_CHECK);
3364 /* Non-matching names never conflict */
3365 if (strcmp(ccon->name, name) != 0)
3366 continue;
3368 if (equal(expr, ccon->expr))
3370 /* OK to merge constraint with existing */
3371 ccon->inhcount++;
3372 if (ccon->inhcount < 0)
3373 ereport(ERROR,
3374 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3375 errmsg("too many inheritance parents"));
3376 return constraints;
3379 ereport(ERROR,
3380 (errcode(ERRCODE_DUPLICATE_OBJECT),
3381 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3382 name)));
3386 * Constraint couldn't be merged with an existing one and also didn't
3387 * conflict with an existing one, so add it as a new one to the list.
3389 newcon = palloc0_object(CookedConstraint);
3390 newcon->contype = CONSTR_CHECK;
3391 newcon->name = pstrdup(name);
3392 newcon->expr = expr;
3393 newcon->inhcount = 1;
3394 return lappend(constraints, newcon);
3399 * StoreCatalogInheritance
3400 * Updates the system catalogs with proper inheritance information.
3402 * supers is a list of the OIDs of the new relation's direct ancestors.
3404 static void
3405 StoreCatalogInheritance(Oid relationId, List *supers,
3406 bool child_is_partition)
3408 Relation relation;
3409 int32 seqNumber;
3410 ListCell *entry;
3413 * sanity checks
3415 Assert(OidIsValid(relationId));
3417 if (supers == NIL)
3418 return;
3421 * Store INHERITS information in pg_inherits using direct ancestors only.
3422 * Also enter dependencies on the direct ancestors, and make sure they are
3423 * marked with relhassubclass = true.
3425 * (Once upon a time, both direct and indirect ancestors were found here
3426 * and then entered into pg_ipl. Since that catalog doesn't exist
3427 * anymore, there's no need to look for indirect ancestors.)
3429 relation = table_open(InheritsRelationId, RowExclusiveLock);
3431 seqNumber = 1;
3432 foreach(entry, supers)
3434 Oid parentOid = lfirst_oid(entry);
3436 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3437 child_is_partition);
3438 seqNumber++;
3441 table_close(relation, RowExclusiveLock);
3445 * Make catalog entries showing relationId as being an inheritance child
3446 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3448 static void
3449 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3450 int32 seqNumber, Relation inhRelation,
3451 bool child_is_partition)
3453 ObjectAddress childobject,
3454 parentobject;
3456 /* store the pg_inherits row */
3457 StoreSingleInheritance(relationId, parentOid, seqNumber);
3460 * Store a dependency too
3462 parentobject.classId = RelationRelationId;
3463 parentobject.objectId = parentOid;
3464 parentobject.objectSubId = 0;
3465 childobject.classId = RelationRelationId;
3466 childobject.objectId = relationId;
3467 childobject.objectSubId = 0;
3469 recordDependencyOn(&childobject, &parentobject,
3470 child_dependency_type(child_is_partition));
3473 * Post creation hook of this inheritance. Since object_access_hook
3474 * doesn't take multiple object identifiers, we relay oid of parent
3475 * relation using auxiliary_id argument.
3477 InvokeObjectPostAlterHookArg(InheritsRelationId,
3478 relationId, 0,
3479 parentOid, false);
3482 * Mark the parent as having subclasses.
3484 SetRelationHasSubclass(parentOid, true);
3488 * Look for an existing column entry with the given name.
3490 * Returns the index (starting with 1) if attribute already exists in columns,
3491 * 0 if it doesn't.
3493 static int
3494 findAttrByName(const char *attributeName, const List *columns)
3496 ListCell *lc;
3497 int i = 1;
3499 foreach(lc, columns)
3501 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3502 return i;
3504 i++;
3506 return 0;
3511 * SetRelationHasSubclass
3512 * Set the value of the relation's relhassubclass field in pg_class.
3514 * NOTE: caller must be holding an appropriate lock on the relation.
3515 * ShareUpdateExclusiveLock is sufficient.
3517 * NOTE: an important side-effect of this operation is that an SI invalidation
3518 * message is sent out to all backends --- including me --- causing plans
3519 * referencing the relation to be rebuilt with the new list of children.
3520 * This must happen even if we find that no change is needed in the pg_class
3521 * row.
3523 void
3524 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3526 Relation relationRelation;
3527 HeapTuple tuple;
3528 Form_pg_class classtuple;
3531 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3533 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3534 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3535 if (!HeapTupleIsValid(tuple))
3536 elog(ERROR, "cache lookup failed for relation %u", relationId);
3537 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3539 if (classtuple->relhassubclass != relhassubclass)
3541 classtuple->relhassubclass = relhassubclass;
3542 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3544 else
3546 /* no need to change tuple, but force relcache rebuild anyway */
3547 CacheInvalidateRelcacheByTuple(tuple);
3550 heap_freetuple(tuple);
3551 table_close(relationRelation, RowExclusiveLock);
3555 * CheckRelationTableSpaceMove
3556 * Check if relation can be moved to new tablespace.
3558 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3560 * Returns true if the relation can be moved to the new tablespace; raises
3561 * an error if it is not possible to do the move; returns false if the move
3562 * would have no effect.
3564 bool
3565 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3567 Oid oldTableSpaceId;
3570 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3571 * stored as 0.
3573 oldTableSpaceId = rel->rd_rel->reltablespace;
3574 if (newTableSpaceId == oldTableSpaceId ||
3575 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3576 return false;
3579 * We cannot support moving mapped relations into different tablespaces.
3580 * (In particular this eliminates all shared catalogs.)
3582 if (RelationIsMapped(rel))
3583 ereport(ERROR,
3584 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3585 errmsg("cannot move system relation \"%s\"",
3586 RelationGetRelationName(rel))));
3588 /* Cannot move a non-shared relation into pg_global */
3589 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3590 ereport(ERROR,
3591 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3592 errmsg("only shared relations can be placed in pg_global tablespace")));
3595 * Do not allow moving temp tables of other backends ... their local
3596 * buffer manager is not going to cope.
3598 if (RELATION_IS_OTHER_TEMP(rel))
3599 ereport(ERROR,
3600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3601 errmsg("cannot move temporary tables of other sessions")));
3603 return true;
3607 * SetRelationTableSpace
3608 * Set new reltablespace and relfilenumber in pg_class entry.
3610 * newTableSpaceId is the new tablespace for the relation, and
3611 * newRelFilenumber its new filenumber. If newRelFilenumber is
3612 * InvalidRelFileNumber, this field is not updated.
3614 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3616 * The caller of this routine had better check if a relation can be
3617 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3618 * first, and is responsible for making the change visible with
3619 * CommandCounterIncrement().
3621 void
3622 SetRelationTableSpace(Relation rel,
3623 Oid newTableSpaceId,
3624 RelFileNumber newRelFilenumber)
3626 Relation pg_class;
3627 HeapTuple tuple;
3628 Form_pg_class rd_rel;
3629 Oid reloid = RelationGetRelid(rel);
3631 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3633 /* Get a modifiable copy of the relation's pg_class row. */
3634 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3636 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3637 if (!HeapTupleIsValid(tuple))
3638 elog(ERROR, "cache lookup failed for relation %u", reloid);
3639 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3641 /* Update the pg_class row. */
3642 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3643 InvalidOid : newTableSpaceId;
3644 if (RelFileNumberIsValid(newRelFilenumber))
3645 rd_rel->relfilenode = newRelFilenumber;
3646 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3649 * Record dependency on tablespace. This is only required for relations
3650 * that have no physical storage.
3652 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3653 changeDependencyOnTablespace(RelationRelationId, reloid,
3654 rd_rel->reltablespace);
3656 heap_freetuple(tuple);
3657 table_close(pg_class, RowExclusiveLock);
3661 * renameatt_check - basic sanity checks before attribute rename
3663 static void
3664 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3666 char relkind = classform->relkind;
3668 if (classform->reloftype && !recursing)
3669 ereport(ERROR,
3670 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3671 errmsg("cannot rename column of typed table")));
3674 * Renaming the columns of sequences or toast tables doesn't actually
3675 * break anything from the system's point of view, since internal
3676 * references are by attnum. But it doesn't seem right to allow users to
3677 * change names that are hardcoded into the system, hence the following
3678 * restriction.
3680 if (relkind != RELKIND_RELATION &&
3681 relkind != RELKIND_VIEW &&
3682 relkind != RELKIND_MATVIEW &&
3683 relkind != RELKIND_COMPOSITE_TYPE &&
3684 relkind != RELKIND_INDEX &&
3685 relkind != RELKIND_PARTITIONED_INDEX &&
3686 relkind != RELKIND_FOREIGN_TABLE &&
3687 relkind != RELKIND_PARTITIONED_TABLE)
3688 ereport(ERROR,
3689 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3690 errmsg("cannot rename columns of relation \"%s\"",
3691 NameStr(classform->relname)),
3692 errdetail_relkind_not_supported(relkind)));
3695 * permissions checking. only the owner of a class can change its schema.
3697 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3698 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3699 NameStr(classform->relname));
3700 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3701 ereport(ERROR,
3702 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3703 errmsg("permission denied: \"%s\" is a system catalog",
3704 NameStr(classform->relname))));
3708 * renameatt_internal - workhorse for renameatt
3710 * Return value is the attribute number in the 'myrelid' relation.
3712 static AttrNumber
3713 renameatt_internal(Oid myrelid,
3714 const char *oldattname,
3715 const char *newattname,
3716 bool recurse,
3717 bool recursing,
3718 int expected_parents,
3719 DropBehavior behavior)
3721 Relation targetrelation;
3722 Relation attrelation;
3723 HeapTuple atttup;
3724 Form_pg_attribute attform;
3725 AttrNumber attnum;
3728 * Grab an exclusive lock on the target table, which we will NOT release
3729 * until end of transaction.
3731 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3732 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3735 * if the 'recurse' flag is set then we are supposed to rename this
3736 * attribute in all classes that inherit from 'relname' (as well as in
3737 * 'relname').
3739 * any permissions or problems with duplicate attributes will cause the
3740 * whole transaction to abort, which is what we want -- all or nothing.
3742 if (recurse)
3744 List *child_oids,
3745 *child_numparents;
3746 ListCell *lo,
3747 *li;
3750 * we need the number of parents for each child so that the recursive
3751 * calls to renameatt() can determine whether there are any parents
3752 * outside the inheritance hierarchy being processed.
3754 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3755 &child_numparents);
3758 * find_all_inheritors does the recursive search of the inheritance
3759 * hierarchy, so all we have to do is process all of the relids in the
3760 * list that it returns.
3762 forboth(lo, child_oids, li, child_numparents)
3764 Oid childrelid = lfirst_oid(lo);
3765 int numparents = lfirst_int(li);
3767 if (childrelid == myrelid)
3768 continue;
3769 /* note we need not recurse again */
3770 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3773 else
3776 * If we are told not to recurse, there had better not be any child
3777 * tables; else the rename would put them out of step.
3779 * expected_parents will only be 0 if we are not already recursing.
3781 if (expected_parents == 0 &&
3782 find_inheritance_children(myrelid, NoLock) != NIL)
3783 ereport(ERROR,
3784 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3785 errmsg("inherited column \"%s\" must be renamed in child tables too",
3786 oldattname)));
3789 /* rename attributes in typed tables of composite type */
3790 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3792 List *child_oids;
3793 ListCell *lo;
3795 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3796 RelationGetRelationName(targetrelation),
3797 behavior);
3799 foreach(lo, child_oids)
3800 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3803 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3805 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3806 if (!HeapTupleIsValid(atttup))
3807 ereport(ERROR,
3808 (errcode(ERRCODE_UNDEFINED_COLUMN),
3809 errmsg("column \"%s\" does not exist",
3810 oldattname)));
3811 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3813 attnum = attform->attnum;
3814 if (attnum <= 0)
3815 ereport(ERROR,
3816 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3817 errmsg("cannot rename system column \"%s\"",
3818 oldattname)));
3821 * if the attribute is inherited, forbid the renaming. if this is a
3822 * top-level call to renameatt(), then expected_parents will be 0, so the
3823 * effect of this code will be to prohibit the renaming if the attribute
3824 * is inherited at all. if this is a recursive call to renameatt(),
3825 * expected_parents will be the number of parents the current relation has
3826 * within the inheritance hierarchy being processed, so we'll prohibit the
3827 * renaming only if there are additional parents from elsewhere.
3829 if (attform->attinhcount > expected_parents)
3830 ereport(ERROR,
3831 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3832 errmsg("cannot rename inherited column \"%s\"",
3833 oldattname)));
3835 /* new name should not already exist */
3836 (void) check_for_column_name_collision(targetrelation, newattname, false);
3838 /* apply the update */
3839 namestrcpy(&(attform->attname), newattname);
3841 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3843 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3845 heap_freetuple(atttup);
3847 table_close(attrelation, RowExclusiveLock);
3849 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3851 return attnum;
3855 * Perform permissions and integrity checks before acquiring a relation lock.
3857 static void
3858 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3859 void *arg)
3861 HeapTuple tuple;
3862 Form_pg_class form;
3864 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3865 if (!HeapTupleIsValid(tuple))
3866 return; /* concurrently dropped */
3867 form = (Form_pg_class) GETSTRUCT(tuple);
3868 renameatt_check(relid, form, false);
3869 ReleaseSysCache(tuple);
3873 * renameatt - changes the name of an attribute in a relation
3875 * The returned ObjectAddress is that of the renamed column.
3877 ObjectAddress
3878 renameatt(RenameStmt *stmt)
3880 Oid relid;
3881 AttrNumber attnum;
3882 ObjectAddress address;
3884 /* lock level taken here should match renameatt_internal */
3885 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3886 stmt->missing_ok ? RVR_MISSING_OK : 0,
3887 RangeVarCallbackForRenameAttribute,
3888 NULL);
3890 if (!OidIsValid(relid))
3892 ereport(NOTICE,
3893 (errmsg("relation \"%s\" does not exist, skipping",
3894 stmt->relation->relname)));
3895 return InvalidObjectAddress;
3898 attnum =
3899 renameatt_internal(relid,
3900 stmt->subname, /* old att name */
3901 stmt->newname, /* new att name */
3902 stmt->relation->inh, /* recursive? */
3903 false, /* recursing? */
3904 0, /* expected inhcount */
3905 stmt->behavior);
3907 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3909 return address;
3913 * same logic as renameatt_internal
3915 static ObjectAddress
3916 rename_constraint_internal(Oid myrelid,
3917 Oid mytypid,
3918 const char *oldconname,
3919 const char *newconname,
3920 bool recurse,
3921 bool recursing,
3922 int expected_parents)
3924 Relation targetrelation = NULL;
3925 Oid constraintOid;
3926 HeapTuple tuple;
3927 Form_pg_constraint con;
3928 ObjectAddress address;
3930 Assert(!myrelid || !mytypid);
3932 if (mytypid)
3934 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3936 else
3938 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3941 * don't tell it whether we're recursing; we allow changing typed
3942 * tables here
3944 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3946 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3949 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3950 if (!HeapTupleIsValid(tuple))
3951 elog(ERROR, "cache lookup failed for constraint %u",
3952 constraintOid);
3953 con = (Form_pg_constraint) GETSTRUCT(tuple);
3955 if (myrelid &&
3956 (con->contype == CONSTRAINT_CHECK ||
3957 con->contype == CONSTRAINT_NOTNULL) &&
3958 !con->connoinherit)
3960 if (recurse)
3962 List *child_oids,
3963 *child_numparents;
3964 ListCell *lo,
3965 *li;
3967 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3968 &child_numparents);
3970 forboth(lo, child_oids, li, child_numparents)
3972 Oid childrelid = lfirst_oid(lo);
3973 int numparents = lfirst_int(li);
3975 if (childrelid == myrelid)
3976 continue;
3978 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3981 else
3983 if (expected_parents == 0 &&
3984 find_inheritance_children(myrelid, NoLock) != NIL)
3985 ereport(ERROR,
3986 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3987 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3988 oldconname)));
3991 if (con->coninhcount > expected_parents)
3992 ereport(ERROR,
3993 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3994 errmsg("cannot rename inherited constraint \"%s\"",
3995 oldconname)));
3998 if (con->conindid
3999 && (con->contype == CONSTRAINT_PRIMARY
4000 || con->contype == CONSTRAINT_UNIQUE
4001 || con->contype == CONSTRAINT_EXCLUSION))
4002 /* rename the index; this renames the constraint as well */
4003 RenameRelationInternal(con->conindid, newconname, false, true);
4004 else
4005 RenameConstraintById(constraintOid, newconname);
4007 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4009 ReleaseSysCache(tuple);
4011 if (targetrelation)
4014 * Invalidate relcache so as others can see the new constraint name.
4016 CacheInvalidateRelcache(targetrelation);
4018 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4021 return address;
4024 ObjectAddress
4025 RenameConstraint(RenameStmt *stmt)
4027 Oid relid = InvalidOid;
4028 Oid typid = InvalidOid;
4030 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4032 Relation rel;
4033 HeapTuple tup;
4035 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4036 rel = table_open(TypeRelationId, RowExclusiveLock);
4037 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4038 if (!HeapTupleIsValid(tup))
4039 elog(ERROR, "cache lookup failed for type %u", typid);
4040 checkDomainOwner(tup);
4041 ReleaseSysCache(tup);
4042 table_close(rel, NoLock);
4044 else
4046 /* lock level taken here should match rename_constraint_internal */
4047 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4048 stmt->missing_ok ? RVR_MISSING_OK : 0,
4049 RangeVarCallbackForRenameAttribute,
4050 NULL);
4051 if (!OidIsValid(relid))
4053 ereport(NOTICE,
4054 (errmsg("relation \"%s\" does not exist, skipping",
4055 stmt->relation->relname)));
4056 return InvalidObjectAddress;
4060 return
4061 rename_constraint_internal(relid, typid,
4062 stmt->subname,
4063 stmt->newname,
4064 (stmt->relation &&
4065 stmt->relation->inh), /* recursive? */
4066 false, /* recursing? */
4067 0 /* expected inhcount */ );
4071 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4072 * RENAME
4074 ObjectAddress
4075 RenameRelation(RenameStmt *stmt)
4077 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4078 Oid relid;
4079 ObjectAddress address;
4082 * Grab an exclusive lock on the target table, index, sequence, view,
4083 * materialized view, or foreign table, which we will NOT release until
4084 * end of transaction.
4086 * Lock level used here should match RenameRelationInternal, to avoid lock
4087 * escalation. However, because ALTER INDEX can be used with any relation
4088 * type, we mustn't believe without verification.
4090 for (;;)
4092 LOCKMODE lockmode;
4093 char relkind;
4094 bool obj_is_index;
4096 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4098 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4099 stmt->missing_ok ? RVR_MISSING_OK : 0,
4100 RangeVarCallbackForAlterRelation,
4101 (void *) stmt);
4103 if (!OidIsValid(relid))
4105 ereport(NOTICE,
4106 (errmsg("relation \"%s\" does not exist, skipping",
4107 stmt->relation->relname)));
4108 return InvalidObjectAddress;
4112 * We allow mismatched statement and object types (e.g., ALTER INDEX
4113 * to rename a table), but we might've used the wrong lock level. If
4114 * that happens, retry with the correct lock level. We don't bother
4115 * if we already acquired AccessExclusiveLock with an index, however.
4117 relkind = get_rel_relkind(relid);
4118 obj_is_index = (relkind == RELKIND_INDEX ||
4119 relkind == RELKIND_PARTITIONED_INDEX);
4120 if (obj_is_index || is_index_stmt == obj_is_index)
4121 break;
4123 UnlockRelationOid(relid, lockmode);
4124 is_index_stmt = obj_is_index;
4127 /* Do the work */
4128 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4130 ObjectAddressSet(address, RelationRelationId, relid);
4132 return address;
4136 * RenameRelationInternal - change the name of a relation
4138 void
4139 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4141 Relation targetrelation;
4142 Relation relrelation; /* for RELATION relation */
4143 HeapTuple reltup;
4144 Form_pg_class relform;
4145 Oid namespaceId;
4148 * Grab a lock on the target relation, which we will NOT release until end
4149 * of transaction. We need at least a self-exclusive lock so that
4150 * concurrent DDL doesn't overwrite the rename if they start updating
4151 * while still seeing the old version. The lock also guards against
4152 * triggering relcache reloads in concurrent sessions, which might not
4153 * handle this information changing under them. For indexes, we can use a
4154 * reduced lock level because RelationReloadIndexInfo() handles indexes
4155 * specially.
4157 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4158 namespaceId = RelationGetNamespace(targetrelation);
4161 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4163 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4165 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4166 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4167 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4168 relform = (Form_pg_class) GETSTRUCT(reltup);
4170 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4171 ereport(ERROR,
4172 (errcode(ERRCODE_DUPLICATE_TABLE),
4173 errmsg("relation \"%s\" already exists",
4174 newrelname)));
4177 * RenameRelation is careful not to believe the caller's idea of the
4178 * relation kind being handled. We don't have to worry about this, but
4179 * let's not be totally oblivious to it. We can process an index as
4180 * not-an-index, but not the other way around.
4182 Assert(!is_index ||
4183 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4184 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4187 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4188 * because it's a copy...)
4190 namestrcpy(&(relform->relname), newrelname);
4192 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4194 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4195 InvalidOid, is_internal);
4197 heap_freetuple(reltup);
4198 table_close(relrelation, RowExclusiveLock);
4201 * Also rename the associated type, if any.
4203 if (OidIsValid(targetrelation->rd_rel->reltype))
4204 RenameTypeInternal(targetrelation->rd_rel->reltype,
4205 newrelname, namespaceId);
4208 * Also rename the associated constraint, if any.
4210 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4211 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4213 Oid constraintId = get_index_constraint(myrelid);
4215 if (OidIsValid(constraintId))
4216 RenameConstraintById(constraintId, newrelname);
4220 * Close rel, but keep lock!
4222 relation_close(targetrelation, NoLock);
4226 * ResetRelRewrite - reset relrewrite
4228 void
4229 ResetRelRewrite(Oid myrelid)
4231 Relation relrelation; /* for RELATION relation */
4232 HeapTuple reltup;
4233 Form_pg_class relform;
4236 * Find relation's pg_class tuple.
4238 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4240 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4241 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4242 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4243 relform = (Form_pg_class) GETSTRUCT(reltup);
4246 * Update pg_class tuple.
4248 relform->relrewrite = InvalidOid;
4250 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4252 heap_freetuple(reltup);
4253 table_close(relrelation, RowExclusiveLock);
4257 * Disallow ALTER TABLE (and similar commands) when the current backend has
4258 * any open reference to the target table besides the one just acquired by
4259 * the calling command; this implies there's an open cursor or active plan.
4260 * We need this check because our lock doesn't protect us against stomping
4261 * on our own foot, only other people's feet!
4263 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4264 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4265 * possibly be relaxed to only error out for certain types of alterations.
4266 * But the use-case for allowing any of these things is not obvious, so we
4267 * won't work hard at it for now.
4269 * We also reject these commands if there are any pending AFTER trigger events
4270 * for the rel. This is certainly necessary for the rewriting variants of
4271 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4272 * events would try to fetch the wrong tuples. It might be overly cautious
4273 * in other cases, but again it seems better to err on the side of paranoia.
4275 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4276 * we are worried about active indexscans on the index. The trigger-event
4277 * check can be skipped, since we are doing no damage to the parent table.
4279 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4281 void
4282 CheckTableNotInUse(Relation rel, const char *stmt)
4284 int expected_refcnt;
4286 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4287 if (rel->rd_refcnt != expected_refcnt)
4288 ereport(ERROR,
4289 (errcode(ERRCODE_OBJECT_IN_USE),
4290 /* translator: first %s is a SQL command, eg ALTER TABLE */
4291 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4292 stmt, RelationGetRelationName(rel))));
4294 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4295 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4296 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4297 ereport(ERROR,
4298 (errcode(ERRCODE_OBJECT_IN_USE),
4299 /* translator: first %s is a SQL command, eg ALTER TABLE */
4300 errmsg("cannot %s \"%s\" because it has pending trigger events",
4301 stmt, RelationGetRelationName(rel))));
4305 * AlterTableLookupRelation
4306 * Look up, and lock, the OID for the relation named by an alter table
4307 * statement.
4310 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4312 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4313 stmt->missing_ok ? RVR_MISSING_OK : 0,
4314 RangeVarCallbackForAlterRelation,
4315 (void *) stmt);
4319 * AlterTable
4320 * Execute ALTER TABLE, which can be a list of subcommands
4322 * ALTER TABLE is performed in three phases:
4323 * 1. Examine subcommands and perform pre-transformation checking.
4324 * 2. Validate and transform subcommands, and update system catalogs.
4325 * 3. Scan table(s) to check new constraints, and optionally recopy
4326 * the data into new table(s).
4327 * Phase 3 is not performed unless one or more of the subcommands requires
4328 * it. The intention of this design is to allow multiple independent
4329 * updates of the table schema to be performed with only one pass over the
4330 * data.
4332 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4333 * each table to be affected (there may be multiple affected tables if the
4334 * commands traverse a table inheritance hierarchy). Also we do preliminary
4335 * validation of the subcommands. Because earlier subcommands may change
4336 * the catalog state seen by later commands, there are limits to what can
4337 * be done in this phase. Generally, this phase acquires table locks,
4338 * checks permissions and relkind, and recurses to find child tables.
4340 * ATRewriteCatalogs performs phase 2 for each affected table.
4341 * Certain subcommands need to be performed before others to avoid
4342 * unnecessary conflicts; for example, DROP COLUMN should come before
4343 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4344 * lists, one for each logical "pass" of phase 2.
4346 * ATRewriteTables performs phase 3 for those tables that need it.
4348 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4349 * since phase 1 already does it. However, for certain subcommand types
4350 * it is only possible to determine how to recurse at phase 2 time; for
4351 * those cases, phase 1 sets the cmd->recurse flag.
4353 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4354 * the whole operation; we don't have to do anything special to clean up.
4356 * The caller must lock the relation, with an appropriate lock level
4357 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4358 * or higher. We pass the lock level down
4359 * so that we can apply it recursively to inherited tables. Note that the
4360 * lock level we want as we recurse might well be higher than required for
4361 * that specific subcommand. So we pass down the overall lock requirement,
4362 * rather than reassess it at lower levels.
4364 * The caller also provides a "context" which is to be passed back to
4365 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4366 * Some of the fields therein, such as the relid, are used here as well.
4368 void
4369 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4370 AlterTableUtilityContext *context)
4372 Relation rel;
4374 /* Caller is required to provide an adequate lock. */
4375 rel = relation_open(context->relid, NoLock);
4377 CheckTableNotInUse(rel, "ALTER TABLE");
4379 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4383 * AlterTableInternal
4385 * ALTER TABLE with target specified by OID
4387 * We do not reject if the relation is already open, because it's quite
4388 * likely that one or more layers of caller have it open. That means it
4389 * is unsafe to use this entry point for alterations that could break
4390 * existing query plans. On the assumption it's not used for such, we
4391 * don't have to reject pending AFTER triggers, either.
4393 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4394 * used for any subcommand types that require parse transformation or
4395 * could generate subcommands that have to be passed to ProcessUtility.
4397 void
4398 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4400 Relation rel;
4401 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4403 rel = relation_open(relid, lockmode);
4405 EventTriggerAlterTableRelid(relid);
4407 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4411 * AlterTableGetLockLevel
4413 * Sets the overall lock level required for the supplied list of subcommands.
4414 * Policy for doing this set according to needs of AlterTable(), see
4415 * comments there for overall explanation.
4417 * Function is called before and after parsing, so it must give same
4418 * answer each time it is called. Some subcommands are transformed
4419 * into other subcommand types, so the transform must never be made to a
4420 * lower lock level than previously assigned. All transforms are noted below.
4422 * Since this is called before we lock the table we cannot use table metadata
4423 * to influence the type of lock we acquire.
4425 * There should be no lockmodes hardcoded into the subcommand functions. All
4426 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4427 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4428 * and does not travel through this section of code and cannot be combined with
4429 * any of the subcommands given here.
4431 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4432 * so any changes that might affect SELECTs running on standbys need to use
4433 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4434 * have a solution for that also.
4436 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4437 * that takes a lock less than AccessExclusiveLock can change object definitions
4438 * while pg_dump is running. Be careful to check that the appropriate data is
4439 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4440 * otherwise we might end up with an inconsistent dump that can't restore.
4442 LOCKMODE
4443 AlterTableGetLockLevel(List *cmds)
4446 * This only works if we read catalog tables using MVCC snapshots.
4448 ListCell *lcmd;
4449 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4451 foreach(lcmd, cmds)
4453 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4454 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4456 switch (cmd->subtype)
4459 * These subcommands rewrite the heap, so require full locks.
4461 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4462 * to SELECT */
4463 case AT_SetAccessMethod: /* must rewrite heap */
4464 case AT_SetTableSpace: /* must rewrite heap */
4465 case AT_AlterColumnType: /* must rewrite heap */
4466 cmd_lockmode = AccessExclusiveLock;
4467 break;
4470 * These subcommands may require addition of toast tables. If
4471 * we add a toast table to a table currently being scanned, we
4472 * might miss data added to the new toast table by concurrent
4473 * insert transactions.
4475 case AT_SetStorage: /* may add toast tables, see
4476 * ATRewriteCatalogs() */
4477 cmd_lockmode = AccessExclusiveLock;
4478 break;
4481 * Removing constraints can affect SELECTs that have been
4482 * optimized assuming the constraint holds true. See also
4483 * CloneFkReferenced.
4485 case AT_DropConstraint: /* as DROP INDEX */
4486 case AT_DropNotNull: /* may change some SQL plans */
4487 cmd_lockmode = AccessExclusiveLock;
4488 break;
4491 * Subcommands that may be visible to concurrent SELECTs
4493 case AT_DropColumn: /* change visible to SELECT */
4494 case AT_AddColumnToView: /* CREATE VIEW */
4495 case AT_DropOids: /* used to equiv to DropColumn */
4496 case AT_EnableAlwaysRule: /* may change SELECT rules */
4497 case AT_EnableReplicaRule: /* may change SELECT rules */
4498 case AT_EnableRule: /* may change SELECT rules */
4499 case AT_DisableRule: /* may change SELECT rules */
4500 cmd_lockmode = AccessExclusiveLock;
4501 break;
4504 * Changing owner may remove implicit SELECT privileges
4506 case AT_ChangeOwner: /* change visible to SELECT */
4507 cmd_lockmode = AccessExclusiveLock;
4508 break;
4511 * Changing foreign table options may affect optimization.
4513 case AT_GenericOptions:
4514 case AT_AlterColumnGenericOptions:
4515 cmd_lockmode = AccessExclusiveLock;
4516 break;
4519 * These subcommands affect write operations only.
4521 case AT_EnableTrig:
4522 case AT_EnableAlwaysTrig:
4523 case AT_EnableReplicaTrig:
4524 case AT_EnableTrigAll:
4525 case AT_EnableTrigUser:
4526 case AT_DisableTrig:
4527 case AT_DisableTrigAll:
4528 case AT_DisableTrigUser:
4529 cmd_lockmode = ShareRowExclusiveLock;
4530 break;
4533 * These subcommands affect write operations only. XXX
4534 * Theoretically, these could be ShareRowExclusiveLock.
4536 case AT_ColumnDefault:
4537 case AT_CookedColumnDefault:
4538 case AT_AlterConstraint:
4539 case AT_AddIndex: /* from ADD CONSTRAINT */
4540 case AT_AddIndexConstraint:
4541 case AT_ReplicaIdentity:
4542 case AT_SetNotNull:
4543 case AT_SetAttNotNull:
4544 case AT_EnableRowSecurity:
4545 case AT_DisableRowSecurity:
4546 case AT_ForceRowSecurity:
4547 case AT_NoForceRowSecurity:
4548 case AT_AddIdentity:
4549 case AT_DropIdentity:
4550 case AT_SetIdentity:
4551 case AT_DropExpression:
4552 case AT_SetCompression:
4553 cmd_lockmode = AccessExclusiveLock;
4554 break;
4556 case AT_AddConstraint:
4557 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4558 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4559 if (IsA(cmd->def, Constraint))
4561 Constraint *con = (Constraint *) cmd->def;
4563 switch (con->contype)
4565 case CONSTR_EXCLUSION:
4566 case CONSTR_PRIMARY:
4567 case CONSTR_UNIQUE:
4570 * Cases essentially the same as CREATE INDEX. We
4571 * could reduce the lock strength to ShareLock if
4572 * we can work out how to allow concurrent catalog
4573 * updates. XXX Might be set down to
4574 * ShareRowExclusiveLock but requires further
4575 * analysis.
4577 cmd_lockmode = AccessExclusiveLock;
4578 break;
4579 case CONSTR_FOREIGN:
4582 * We add triggers to both tables when we add a
4583 * Foreign Key, so the lock level must be at least
4584 * as strong as CREATE TRIGGER.
4586 cmd_lockmode = ShareRowExclusiveLock;
4587 break;
4589 default:
4590 cmd_lockmode = AccessExclusiveLock;
4593 break;
4596 * These subcommands affect inheritance behaviour. Queries
4597 * started before us will continue to see the old inheritance
4598 * behaviour, while queries started after we commit will see
4599 * new behaviour. No need to prevent reads or writes to the
4600 * subtable while we hook it up though. Changing the TupDesc
4601 * may be a problem, so keep highest lock.
4603 case AT_AddInherit:
4604 case AT_DropInherit:
4605 cmd_lockmode = AccessExclusiveLock;
4606 break;
4609 * These subcommands affect implicit row type conversion. They
4610 * have affects similar to CREATE/DROP CAST on queries. don't
4611 * provide for invalidating parse trees as a result of such
4612 * changes, so we keep these at AccessExclusiveLock.
4614 case AT_AddOf:
4615 case AT_DropOf:
4616 cmd_lockmode = AccessExclusiveLock;
4617 break;
4620 * Only used by CREATE OR REPLACE VIEW which must conflict
4621 * with an SELECTs currently using the view.
4623 case AT_ReplaceRelOptions:
4624 cmd_lockmode = AccessExclusiveLock;
4625 break;
4628 * These subcommands affect general strategies for performance
4629 * and maintenance, though don't change the semantic results
4630 * from normal data reads and writes. Delaying an ALTER TABLE
4631 * behind currently active writes only delays the point where
4632 * the new strategy begins to take effect, so there is no
4633 * benefit in waiting. In this case the minimum restriction
4634 * applies: we don't currently allow concurrent catalog
4635 * updates.
4637 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4638 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4639 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4640 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4641 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4642 cmd_lockmode = ShareUpdateExclusiveLock;
4643 break;
4645 case AT_SetLogged:
4646 case AT_SetUnLogged:
4647 cmd_lockmode = AccessExclusiveLock;
4648 break;
4650 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4651 cmd_lockmode = ShareUpdateExclusiveLock;
4652 break;
4655 * Rel options are more complex than first appears. Options
4656 * are set here for tables, views and indexes; for historical
4657 * reasons these can all be used with ALTER TABLE, so we can't
4658 * decide between them using the basic grammar.
4660 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4661 * getTables() */
4662 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4663 * getTables() */
4664 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4665 break;
4667 case AT_AttachPartition:
4668 cmd_lockmode = ShareUpdateExclusiveLock;
4669 break;
4671 case AT_DetachPartition:
4672 if (((PartitionCmd *) cmd->def)->concurrent)
4673 cmd_lockmode = ShareUpdateExclusiveLock;
4674 else
4675 cmd_lockmode = AccessExclusiveLock;
4676 break;
4678 case AT_DetachPartitionFinalize:
4679 cmd_lockmode = ShareUpdateExclusiveLock;
4680 break;
4682 default: /* oops */
4683 elog(ERROR, "unrecognized alter table type: %d",
4684 (int) cmd->subtype);
4685 break;
4689 * Take the greatest lockmode from any subcommand
4691 if (cmd_lockmode > lockmode)
4692 lockmode = cmd_lockmode;
4695 return lockmode;
4699 * ATController provides top level control over the phases.
4701 * parsetree is passed in to allow it to be passed to event triggers
4702 * when requested.
4704 static void
4705 ATController(AlterTableStmt *parsetree,
4706 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4707 AlterTableUtilityContext *context)
4709 List *wqueue = NIL;
4710 ListCell *lcmd;
4712 /* Phase 1: preliminary examination of commands, create work queue */
4713 foreach(lcmd, cmds)
4715 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4717 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4720 /* Close the relation, but keep lock until commit */
4721 relation_close(rel, NoLock);
4723 /* Phase 2: update system catalogs */
4724 ATRewriteCatalogs(&wqueue, lockmode, context);
4726 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4727 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4731 * ATPrepCmd
4733 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4734 * recursion and permission checks.
4736 * Caller must have acquired appropriate lock type on relation already.
4737 * This lock should be held until commit.
4739 static void
4740 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4741 bool recurse, bool recursing, LOCKMODE lockmode,
4742 AlterTableUtilityContext *context)
4744 AlteredTableInfo *tab;
4745 int pass = AT_PASS_UNSET;
4747 /* Find or create work queue entry for this table */
4748 tab = ATGetQueueEntry(wqueue, rel);
4751 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4752 * partitions that are pending detach.
4754 if (rel->rd_rel->relispartition &&
4755 cmd->subtype != AT_DetachPartitionFinalize &&
4756 PartitionHasPendingDetach(RelationGetRelid(rel)))
4757 ereport(ERROR,
4758 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4759 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4760 RelationGetRelationName(rel)),
4761 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4764 * Copy the original subcommand for each table, so we can scribble on it.
4765 * This avoids conflicts when different child tables need to make
4766 * different parse transformations (for example, the same column may have
4767 * different column numbers in different children).
4769 cmd = copyObject(cmd);
4772 * Do permissions and relkind checking, recursion to child tables if
4773 * needed, and any additional phase-1 processing needed. (But beware of
4774 * adding any processing that looks at table details that another
4775 * subcommand could change. In some cases we reject multiple subcommands
4776 * that could try to change the same state in contrary ways.)
4778 switch (cmd->subtype)
4780 case AT_AddColumn: /* ADD COLUMN */
4781 ATSimplePermissions(cmd->subtype, rel,
4782 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4783 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4784 lockmode, context);
4785 /* Recursion occurs during execution phase */
4786 pass = AT_PASS_ADD_COL;
4787 break;
4788 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4789 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4790 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4791 lockmode, context);
4792 /* Recursion occurs during execution phase */
4793 pass = AT_PASS_ADD_COL;
4794 break;
4795 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4798 * We allow defaults on views so that INSERT into a view can have
4799 * default-ish behavior. This works because the rewriter
4800 * substitutes default values into INSERTs before it expands
4801 * rules.
4803 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4804 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4805 /* No command-specific prep needed */
4806 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4807 break;
4808 case AT_CookedColumnDefault: /* add a pre-cooked default */
4809 /* This is currently used only in CREATE TABLE */
4810 /* (so the permission check really isn't necessary) */
4811 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4812 /* This command never recurses */
4813 pass = AT_PASS_ADD_OTHERCONSTR;
4814 break;
4815 case AT_AddIdentity:
4816 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4817 /* This command never recurses */
4818 pass = AT_PASS_ADD_OTHERCONSTR;
4819 break;
4820 case AT_SetIdentity:
4821 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4822 /* This command never recurses */
4823 /* This should run after AddIdentity, so do it in MISC pass */
4824 pass = AT_PASS_MISC;
4825 break;
4826 case AT_DropIdentity:
4827 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4828 /* This command never recurses */
4829 pass = AT_PASS_DROP;
4830 break;
4831 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4832 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4833 /* Set up recursion for phase 2; no other prep needed */
4834 if (recurse)
4835 cmd->recurse = true;
4836 pass = AT_PASS_DROP;
4837 break;
4838 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4839 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4840 /* Set up recursion for phase 2; no other prep needed */
4841 if (recurse)
4842 cmd->recurse = true;
4843 pass = AT_PASS_COL_ATTRS;
4844 break;
4845 case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4846 * a constraint */
4847 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4848 /* Need command-specific recursion decision */
4849 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4850 pass = AT_PASS_COL_ATTRS;
4851 break;
4852 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4853 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4854 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4855 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4856 pass = AT_PASS_DROP;
4857 break;
4858 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4859 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4860 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4861 /* No command-specific prep needed */
4862 pass = AT_PASS_MISC;
4863 break;
4864 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4865 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4866 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4867 /* This command never recurses */
4868 pass = AT_PASS_MISC;
4869 break;
4870 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4871 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4872 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4873 /* No command-specific prep needed */
4874 pass = AT_PASS_MISC;
4875 break;
4876 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4877 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4878 /* This command never recurses */
4879 /* No command-specific prep needed */
4880 pass = AT_PASS_MISC;
4881 break;
4882 case AT_DropColumn: /* DROP COLUMN */
4883 ATSimplePermissions(cmd->subtype, rel,
4884 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4885 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4886 lockmode, context);
4887 /* Recursion occurs during execution phase */
4888 pass = AT_PASS_DROP;
4889 break;
4890 case AT_AddIndex: /* ADD INDEX */
4891 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4892 /* This command never recurses */
4893 /* No command-specific prep needed */
4894 pass = AT_PASS_ADD_INDEX;
4895 break;
4896 case AT_AddConstraint: /* ADD CONSTRAINT */
4897 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4898 /* Recursion occurs during execution phase */
4899 /* No command-specific prep needed except saving recurse flag */
4900 if (recurse)
4901 cmd->recurse = true;
4902 pass = AT_PASS_ADD_CONSTR;
4903 break;
4904 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4905 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4906 /* This command never recurses */
4907 /* No command-specific prep needed */
4908 pass = AT_PASS_ADD_INDEXCONSTR;
4909 break;
4910 case AT_DropConstraint: /* DROP CONSTRAINT */
4911 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4912 ATCheckPartitionsNotInUse(rel, lockmode);
4913 /* Other recursion occurs during execution phase */
4914 /* No command-specific prep needed except saving recurse flag */
4915 if (recurse)
4916 cmd->recurse = true;
4917 pass = AT_PASS_DROP;
4918 break;
4919 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4920 ATSimplePermissions(cmd->subtype, rel,
4921 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4922 /* See comments for ATPrepAlterColumnType */
4923 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4924 AT_PASS_UNSET, context);
4925 Assert(cmd != NULL);
4926 /* Performs own recursion */
4927 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4928 lockmode, context);
4929 pass = AT_PASS_ALTER_TYPE;
4930 break;
4931 case AT_AlterColumnGenericOptions:
4932 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4933 /* This command never recurses */
4934 /* No command-specific prep needed */
4935 pass = AT_PASS_MISC;
4936 break;
4937 case AT_ChangeOwner: /* ALTER OWNER */
4938 /* This command never recurses */
4939 /* No command-specific prep needed */
4940 pass = AT_PASS_MISC;
4941 break;
4942 case AT_ClusterOn: /* CLUSTER ON */
4943 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4944 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4945 /* These commands never recurse */
4946 /* No command-specific prep needed */
4947 pass = AT_PASS_MISC;
4948 break;
4949 case AT_SetLogged: /* SET LOGGED */
4950 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4951 if (tab->chgPersistence)
4952 ereport(ERROR,
4953 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4954 errmsg("cannot change persistence setting twice")));
4955 tab->chgPersistence = ATPrepChangePersistence(rel, true);
4956 /* force rewrite if necessary; see comment in ATRewriteTables */
4957 if (tab->chgPersistence)
4959 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4960 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4962 pass = AT_PASS_MISC;
4963 break;
4964 case AT_SetUnLogged: /* SET UNLOGGED */
4965 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4966 if (tab->chgPersistence)
4967 ereport(ERROR,
4968 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4969 errmsg("cannot change persistence setting twice")));
4970 tab->chgPersistence = ATPrepChangePersistence(rel, false);
4971 /* force rewrite if necessary; see comment in ATRewriteTables */
4972 if (tab->chgPersistence)
4974 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4975 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4977 pass = AT_PASS_MISC;
4978 break;
4979 case AT_DropOids: /* SET WITHOUT OIDS */
4980 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4981 pass = AT_PASS_DROP;
4982 break;
4983 case AT_SetAccessMethod: /* SET ACCESS METHOD */
4984 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4986 /* partitioned tables don't have an access method */
4987 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4988 ereport(ERROR,
4989 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
4990 errmsg("cannot change access method of a partitioned table")));
4992 /* check if another access method change was already requested */
4993 if (OidIsValid(tab->newAccessMethod))
4994 ereport(ERROR,
4995 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4996 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
4998 ATPrepSetAccessMethod(tab, rel, cmd->name);
4999 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5000 break;
5001 case AT_SetTableSpace: /* SET TABLESPACE */
5002 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5003 ATT_PARTITIONED_INDEX);
5004 /* This command never recurses */
5005 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5006 pass = AT_PASS_MISC; /* doesn't actually matter */
5007 break;
5008 case AT_SetRelOptions: /* SET (...) */
5009 case AT_ResetRelOptions: /* RESET (...) */
5010 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5011 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5012 /* This command never recurses */
5013 /* No command-specific prep needed */
5014 pass = AT_PASS_MISC;
5015 break;
5016 case AT_AddInherit: /* INHERIT */
5017 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5018 /* This command never recurses */
5019 ATPrepAddInherit(rel);
5020 pass = AT_PASS_MISC;
5021 break;
5022 case AT_DropInherit: /* NO INHERIT */
5023 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5024 /* This command never recurses */
5025 /* No command-specific prep needed */
5026 pass = AT_PASS_MISC;
5027 break;
5028 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5029 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5030 /* Recursion occurs during execution phase */
5031 pass = AT_PASS_MISC;
5032 break;
5033 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5034 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5035 /* Recursion occurs during execution phase */
5036 /* No command-specific prep needed except saving recurse flag */
5037 if (recurse)
5038 cmd->recurse = true;
5039 pass = AT_PASS_MISC;
5040 break;
5041 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5042 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5043 pass = AT_PASS_MISC;
5044 /* This command never recurses */
5045 /* No command-specific prep needed */
5046 break;
5047 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5048 case AT_EnableAlwaysTrig:
5049 case AT_EnableReplicaTrig:
5050 case AT_EnableTrigAll:
5051 case AT_EnableTrigUser:
5052 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5053 case AT_DisableTrigAll:
5054 case AT_DisableTrigUser:
5055 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5056 /* Set up recursion for phase 2; no other prep needed */
5057 if (recurse)
5058 cmd->recurse = true;
5059 pass = AT_PASS_MISC;
5060 break;
5061 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5062 case AT_EnableAlwaysRule:
5063 case AT_EnableReplicaRule:
5064 case AT_DisableRule:
5065 case AT_AddOf: /* OF */
5066 case AT_DropOf: /* NOT OF */
5067 case AT_EnableRowSecurity:
5068 case AT_DisableRowSecurity:
5069 case AT_ForceRowSecurity:
5070 case AT_NoForceRowSecurity:
5071 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5072 /* These commands never recurse */
5073 /* No command-specific prep needed */
5074 pass = AT_PASS_MISC;
5075 break;
5076 case AT_GenericOptions:
5077 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5078 /* No command-specific prep needed */
5079 pass = AT_PASS_MISC;
5080 break;
5081 case AT_AttachPartition:
5082 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5083 /* No command-specific prep needed */
5084 pass = AT_PASS_MISC;
5085 break;
5086 case AT_DetachPartition:
5087 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5088 /* No command-specific prep needed */
5089 pass = AT_PASS_MISC;
5090 break;
5091 case AT_DetachPartitionFinalize:
5092 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5093 /* No command-specific prep needed */
5094 pass = AT_PASS_MISC;
5095 break;
5096 default: /* oops */
5097 elog(ERROR, "unrecognized alter table type: %d",
5098 (int) cmd->subtype);
5099 pass = AT_PASS_UNSET; /* keep compiler quiet */
5100 break;
5102 Assert(pass > AT_PASS_UNSET);
5104 /* Add the subcommand to the appropriate list for phase 2 */
5105 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5109 * ATRewriteCatalogs
5111 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5112 * dispatched in a "safe" execution order (designed to avoid unnecessary
5113 * conflicts).
5115 static void
5116 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5117 AlterTableUtilityContext *context)
5119 int pass;
5120 ListCell *ltab;
5123 * We process all the tables "in parallel", one pass at a time. This is
5124 * needed because we may have to propagate work from one table to another
5125 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5126 * re-adding of the foreign key constraint to the other table). Work can
5127 * only be propagated into later passes, however.
5129 for (pass = 0; pass < AT_NUM_PASSES; pass++)
5131 /* Go through each table that needs to be processed */
5132 foreach(ltab, *wqueue)
5134 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5135 List *subcmds = tab->subcmds[pass];
5136 ListCell *lcmd;
5138 if (subcmds == NIL)
5139 continue;
5142 * Open the relation and store it in tab. This allows subroutines
5143 * close and reopen, if necessary. Appropriate lock was obtained
5144 * by phase 1, needn't get it again.
5146 tab->rel = relation_open(tab->relid, NoLock);
5148 foreach(lcmd, subcmds)
5149 ATExecCmd(wqueue, tab,
5150 lfirst_node(AlterTableCmd, lcmd),
5151 lockmode, pass, context);
5154 * After the ALTER TYPE pass, do cleanup work (this is not done in
5155 * ATExecAlterColumnType since it should be done only once if
5156 * multiple columns of a table are altered).
5158 if (pass == AT_PASS_ALTER_TYPE)
5159 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5161 if (tab->rel)
5163 relation_close(tab->rel, NoLock);
5164 tab->rel = NULL;
5169 /* Check to see if a toast table must be added. */
5170 foreach(ltab, *wqueue)
5172 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5175 * If the table is source table of ATTACH PARTITION command, we did
5176 * not modify anything about it that will change its toasting
5177 * requirement, so no need to check.
5179 if (((tab->relkind == RELKIND_RELATION ||
5180 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5181 tab->partition_constraint == NULL) ||
5182 tab->relkind == RELKIND_MATVIEW)
5183 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5188 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5190 static void
5191 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5192 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
5193 AlterTableUtilityContext *context)
5195 ObjectAddress address = InvalidObjectAddress;
5196 Relation rel = tab->rel;
5198 switch (cmd->subtype)
5200 case AT_AddColumn: /* ADD COLUMN */
5201 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5202 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5203 cmd->recurse, false,
5204 lockmode, cur_pass, context);
5205 break;
5206 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5207 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5208 break;
5209 case AT_CookedColumnDefault: /* add a pre-cooked default */
5210 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5211 break;
5212 case AT_AddIdentity:
5213 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5214 cur_pass, context);
5215 Assert(cmd != NULL);
5216 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
5217 break;
5218 case AT_SetIdentity:
5219 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5220 cur_pass, context);
5221 Assert(cmd != NULL);
5222 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
5223 break;
5224 case AT_DropIdentity:
5225 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
5226 break;
5227 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5228 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5229 break;
5230 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5231 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5232 cmd->recurse, false, NULL, lockmode);
5233 break;
5234 case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5235 address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5236 break;
5237 case AT_DropExpression:
5238 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5239 break;
5240 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5241 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5242 break;
5243 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5244 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5245 break;
5246 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5247 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5248 break;
5249 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5250 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5251 break;
5252 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5253 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5254 lockmode);
5255 break;
5256 case AT_DropColumn: /* DROP COLUMN */
5257 address = ATExecDropColumn(wqueue, rel, cmd->name,
5258 cmd->behavior, cmd->recurse, false,
5259 cmd->missing_ok, lockmode,
5260 NULL);
5261 break;
5262 case AT_AddIndex: /* ADD INDEX */
5263 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5264 lockmode);
5265 break;
5266 case AT_ReAddIndex: /* ADD INDEX */
5267 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5268 lockmode);
5269 break;
5270 case AT_ReAddStatistics: /* ADD STATISTICS */
5271 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5272 true, lockmode);
5273 break;
5274 case AT_AddConstraint: /* ADD CONSTRAINT */
5275 /* Transform the command only during initial examination */
5276 if (cur_pass == AT_PASS_ADD_CONSTR)
5277 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5278 cmd->recurse, lockmode,
5279 cur_pass, context);
5280 /* Depending on constraint type, might be no more work to do now */
5281 if (cmd != NULL)
5282 address =
5283 ATExecAddConstraint(wqueue, tab, rel,
5284 (Constraint *) cmd->def,
5285 cmd->recurse, false, lockmode);
5286 break;
5287 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5288 address =
5289 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5290 true, true, lockmode);
5291 break;
5292 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5293 * constraint */
5294 address =
5295 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5296 ((AlterDomainStmt *) cmd->def)->def,
5297 NULL);
5298 break;
5299 case AT_ReAddComment: /* Re-add existing comment */
5300 address = CommentObject((CommentStmt *) cmd->def);
5301 break;
5302 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5303 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5304 lockmode);
5305 break;
5306 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5307 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5308 break;
5309 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5310 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5311 false, lockmode);
5312 break;
5313 case AT_DropConstraint: /* DROP CONSTRAINT */
5314 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5315 cmd->recurse,
5316 cmd->missing_ok, lockmode);
5317 break;
5318 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5319 /* parse transformation was done earlier */
5320 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5321 break;
5322 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5323 address =
5324 ATExecAlterColumnGenericOptions(rel, cmd->name,
5325 (List *) cmd->def, lockmode);
5326 break;
5327 case AT_ChangeOwner: /* ALTER OWNER */
5328 ATExecChangeOwner(RelationGetRelid(rel),
5329 get_rolespec_oid(cmd->newowner, false),
5330 false, lockmode);
5331 break;
5332 case AT_ClusterOn: /* CLUSTER ON */
5333 address = ATExecClusterOn(rel, cmd->name, lockmode);
5334 break;
5335 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5336 ATExecDropCluster(rel, lockmode);
5337 break;
5338 case AT_SetLogged: /* SET LOGGED */
5339 case AT_SetUnLogged: /* SET UNLOGGED */
5340 break;
5341 case AT_DropOids: /* SET WITHOUT OIDS */
5342 /* nothing to do here, oid columns don't exist anymore */
5343 break;
5344 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5345 /* handled specially in Phase 3 */
5346 break;
5347 case AT_SetTableSpace: /* SET TABLESPACE */
5350 * Only do this for partitioned tables and indexes, for which this
5351 * is just a catalog change. Other relation types which have
5352 * storage are handled by Phase 3.
5354 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5355 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5356 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5358 break;
5359 case AT_SetRelOptions: /* SET (...) */
5360 case AT_ResetRelOptions: /* RESET (...) */
5361 case AT_ReplaceRelOptions: /* replace entire option list */
5362 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5363 break;
5364 case AT_EnableTrig: /* ENABLE TRIGGER name */
5365 ATExecEnableDisableTrigger(rel, cmd->name,
5366 TRIGGER_FIRES_ON_ORIGIN, false,
5367 cmd->recurse,
5368 lockmode);
5369 break;
5370 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5371 ATExecEnableDisableTrigger(rel, cmd->name,
5372 TRIGGER_FIRES_ALWAYS, false,
5373 cmd->recurse,
5374 lockmode);
5375 break;
5376 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5377 ATExecEnableDisableTrigger(rel, cmd->name,
5378 TRIGGER_FIRES_ON_REPLICA, false,
5379 cmd->recurse,
5380 lockmode);
5381 break;
5382 case AT_DisableTrig: /* DISABLE TRIGGER name */
5383 ATExecEnableDisableTrigger(rel, cmd->name,
5384 TRIGGER_DISABLED, false,
5385 cmd->recurse,
5386 lockmode);
5387 break;
5388 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5389 ATExecEnableDisableTrigger(rel, NULL,
5390 TRIGGER_FIRES_ON_ORIGIN, false,
5391 cmd->recurse,
5392 lockmode);
5393 break;
5394 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5395 ATExecEnableDisableTrigger(rel, NULL,
5396 TRIGGER_DISABLED, false,
5397 cmd->recurse,
5398 lockmode);
5399 break;
5400 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5401 ATExecEnableDisableTrigger(rel, NULL,
5402 TRIGGER_FIRES_ON_ORIGIN, true,
5403 cmd->recurse,
5404 lockmode);
5405 break;
5406 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5407 ATExecEnableDisableTrigger(rel, NULL,
5408 TRIGGER_DISABLED, true,
5409 cmd->recurse,
5410 lockmode);
5411 break;
5413 case AT_EnableRule: /* ENABLE RULE name */
5414 ATExecEnableDisableRule(rel, cmd->name,
5415 RULE_FIRES_ON_ORIGIN, lockmode);
5416 break;
5417 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5418 ATExecEnableDisableRule(rel, cmd->name,
5419 RULE_FIRES_ALWAYS, lockmode);
5420 break;
5421 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5422 ATExecEnableDisableRule(rel, cmd->name,
5423 RULE_FIRES_ON_REPLICA, lockmode);
5424 break;
5425 case AT_DisableRule: /* DISABLE RULE name */
5426 ATExecEnableDisableRule(rel, cmd->name,
5427 RULE_DISABLED, lockmode);
5428 break;
5430 case AT_AddInherit:
5431 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5432 break;
5433 case AT_DropInherit:
5434 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5435 break;
5436 case AT_AddOf:
5437 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5438 break;
5439 case AT_DropOf:
5440 ATExecDropOf(rel, lockmode);
5441 break;
5442 case AT_ReplicaIdentity:
5443 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5444 break;
5445 case AT_EnableRowSecurity:
5446 ATExecSetRowSecurity(rel, true);
5447 break;
5448 case AT_DisableRowSecurity:
5449 ATExecSetRowSecurity(rel, false);
5450 break;
5451 case AT_ForceRowSecurity:
5452 ATExecForceNoForceRowSecurity(rel, true);
5453 break;
5454 case AT_NoForceRowSecurity:
5455 ATExecForceNoForceRowSecurity(rel, false);
5456 break;
5457 case AT_GenericOptions:
5458 ATExecGenericOptions(rel, (List *) cmd->def);
5459 break;
5460 case AT_AttachPartition:
5461 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5462 cur_pass, context);
5463 Assert(cmd != NULL);
5464 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5465 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5466 context);
5467 else
5468 address = ATExecAttachPartitionIdx(wqueue, rel,
5469 ((PartitionCmd *) cmd->def)->name);
5470 break;
5471 case AT_DetachPartition:
5472 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5473 cur_pass, context);
5474 Assert(cmd != NULL);
5475 /* ATPrepCmd ensures it must be a table */
5476 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5477 address = ATExecDetachPartition(wqueue, tab, rel,
5478 ((PartitionCmd *) cmd->def)->name,
5479 ((PartitionCmd *) cmd->def)->concurrent);
5480 break;
5481 case AT_DetachPartitionFinalize:
5482 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5483 break;
5484 default: /* oops */
5485 elog(ERROR, "unrecognized alter table type: %d",
5486 (int) cmd->subtype);
5487 break;
5491 * Report the subcommand to interested event triggers.
5493 if (cmd)
5494 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5497 * Bump the command counter to ensure the next subcommand in the sequence
5498 * can see the changes so far
5500 CommandCounterIncrement();
5504 * ATParseTransformCmd: perform parse transformation for one subcommand
5506 * Returns the transformed subcommand tree, if there is one, else NULL.
5508 * The parser may hand back additional AlterTableCmd(s) and/or other
5509 * utility statements, either before or after the original subcommand.
5510 * Other AlterTableCmds are scheduled into the appropriate slot of the
5511 * AlteredTableInfo (they had better be for later passes than the current one).
5512 * Utility statements that are supposed to happen before the AlterTableCmd
5513 * are executed immediately. Those that are supposed to happen afterwards
5514 * are added to the tab->afterStmts list to be done at the very end.
5516 static AlterTableCmd *
5517 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5518 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5519 int cur_pass, AlterTableUtilityContext *context)
5521 AlterTableCmd *newcmd = NULL;
5522 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5523 List *beforeStmts;
5524 List *afterStmts;
5525 ListCell *lc;
5527 /* Gin up an AlterTableStmt with just this subcommand and this table */
5528 atstmt->relation =
5529 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5530 pstrdup(RelationGetRelationName(rel)),
5531 -1);
5532 atstmt->relation->inh = recurse;
5533 atstmt->cmds = list_make1(cmd);
5534 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5535 atstmt->missing_ok = false;
5537 /* Transform the AlterTableStmt */
5538 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5539 atstmt,
5540 context->queryString,
5541 &beforeStmts,
5542 &afterStmts);
5544 /* Execute any statements that should happen before these subcommand(s) */
5545 foreach(lc, beforeStmts)
5547 Node *stmt = (Node *) lfirst(lc);
5549 ProcessUtilityForAlterTable(stmt, context);
5550 CommandCounterIncrement();
5553 /* Examine the transformed subcommands and schedule them appropriately */
5554 foreach(lc, atstmt->cmds)
5556 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5557 int pass;
5560 * This switch need only cover the subcommand types that can be added
5561 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5562 * executing the subcommand immediately, as a substitute for the
5563 * original subcommand. (Note, however, that this does cause
5564 * AT_AddConstraint subcommands to be rescheduled into later passes,
5565 * which is important for index and foreign key constraints.)
5567 * We assume we needn't do any phase-1 checks for added subcommands.
5569 switch (cmd2->subtype)
5571 case AT_SetAttNotNull:
5572 ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5573 pass = AT_PASS_COL_ATTRS;
5574 break;
5575 case AT_AddIndex:
5578 * A primary key on a inheritance parent needs supporting NOT
5579 * NULL constraint on its children; enqueue commands to create
5580 * those or mark them inherited if they already exist.
5582 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5583 pass = AT_PASS_ADD_INDEX;
5584 break;
5585 case AT_AddIndexConstraint:
5586 /* as above */
5587 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5588 pass = AT_PASS_ADD_INDEXCONSTR;
5589 break;
5590 case AT_AddConstraint:
5591 /* Recursion occurs during execution phase */
5592 if (recurse)
5593 cmd2->recurse = true;
5594 switch (castNode(Constraint, cmd2->def)->contype)
5596 case CONSTR_PRIMARY:
5597 case CONSTR_UNIQUE:
5598 case CONSTR_EXCLUSION:
5599 pass = AT_PASS_ADD_INDEXCONSTR;
5600 break;
5601 default:
5602 pass = AT_PASS_ADD_OTHERCONSTR;
5603 break;
5605 break;
5606 case AT_AlterColumnGenericOptions:
5607 /* This command never recurses */
5608 /* No command-specific prep needed */
5609 pass = AT_PASS_MISC;
5610 break;
5611 default:
5612 pass = cur_pass;
5613 break;
5616 if (pass < cur_pass)
5618 /* Cannot schedule into a pass we already finished */
5619 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5620 pass);
5622 else if (pass > cur_pass)
5624 /* OK, queue it up for later */
5625 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5627 else
5630 * We should see at most one subcommand for the current pass,
5631 * which is the transformed version of the original subcommand.
5633 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5635 /* Found the transformed version of our subcommand */
5636 newcmd = cmd2;
5638 else
5639 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5640 pass);
5644 /* Queue up any after-statements to happen at the end */
5645 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5647 return newcmd;
5651 * ATRewriteTables: ALTER TABLE phase 3
5653 static void
5654 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5655 AlterTableUtilityContext *context)
5657 ListCell *ltab;
5659 /* Go through each table that needs to be checked or rewritten */
5660 foreach(ltab, *wqueue)
5662 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5664 /* Relations without storage may be ignored here */
5665 if (!RELKIND_HAS_STORAGE(tab->relkind))
5666 continue;
5669 * If we change column data types, the operation has to be propagated
5670 * to tables that use this table's rowtype as a column type.
5671 * tab->newvals will also be non-NULL in the case where we're adding a
5672 * column with a default. We choose to forbid that case as well,
5673 * since composite types might eventually support defaults.
5675 * (Eventually we'll probably need to check for composite type
5676 * dependencies even when we're just scanning the table without a
5677 * rewrite, but at the moment a composite type does not enforce any
5678 * constraints, so it's not necessary/appropriate to enforce them just
5679 * during ALTER.)
5681 if (tab->newvals != NIL || tab->rewrite > 0)
5683 Relation rel;
5685 rel = table_open(tab->relid, NoLock);
5686 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5687 table_close(rel, NoLock);
5691 * We only need to rewrite the table if at least one column needs to
5692 * be recomputed, or we are changing its persistence or access method.
5694 * There are two reasons for requiring a rewrite when changing
5695 * persistence: on one hand, we need to ensure that the buffers
5696 * belonging to each of the two relations are marked with or without
5697 * BM_PERMANENT properly. On the other hand, since rewriting creates
5698 * and assigns a new relfilenumber, we automatically create or drop an
5699 * init fork for the relation as appropriate.
5701 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5703 /* Build a temporary relation and copy data */
5704 Relation OldHeap;
5705 Oid OIDNewHeap;
5706 Oid NewAccessMethod;
5707 Oid NewTableSpace;
5708 char persistence;
5710 OldHeap = table_open(tab->relid, NoLock);
5713 * We don't support rewriting of system catalogs; there are too
5714 * many corner cases and too little benefit. In particular this
5715 * is certainly not going to work for mapped catalogs.
5717 if (IsSystemRelation(OldHeap))
5718 ereport(ERROR,
5719 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5720 errmsg("cannot rewrite system relation \"%s\"",
5721 RelationGetRelationName(OldHeap))));
5723 if (RelationIsUsedAsCatalogTable(OldHeap))
5724 ereport(ERROR,
5725 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5726 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5727 RelationGetRelationName(OldHeap))));
5730 * Don't allow rewrite on temp tables of other backends ... their
5731 * local buffer manager is not going to cope.
5733 if (RELATION_IS_OTHER_TEMP(OldHeap))
5734 ereport(ERROR,
5735 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5736 errmsg("cannot rewrite temporary tables of other sessions")));
5739 * Select destination tablespace (same as original unless user
5740 * requested a change)
5742 if (tab->newTableSpace)
5743 NewTableSpace = tab->newTableSpace;
5744 else
5745 NewTableSpace = OldHeap->rd_rel->reltablespace;
5748 * Select destination access method (same as original unless user
5749 * requested a change)
5751 if (OidIsValid(tab->newAccessMethod))
5752 NewAccessMethod = tab->newAccessMethod;
5753 else
5754 NewAccessMethod = OldHeap->rd_rel->relam;
5757 * Select persistence of transient table (same as original unless
5758 * user requested a change)
5760 persistence = tab->chgPersistence ?
5761 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5763 table_close(OldHeap, NoLock);
5766 * Fire off an Event Trigger now, before actually rewriting the
5767 * table.
5769 * We don't support Event Trigger for nested commands anywhere,
5770 * here included, and parsetree is given NULL when coming from
5771 * AlterTableInternal.
5773 * And fire it only once.
5775 if (parsetree)
5776 EventTriggerTableRewrite((Node *) parsetree,
5777 tab->relid,
5778 tab->rewrite);
5781 * Create transient table that will receive the modified data.
5783 * Ensure it is marked correctly as logged or unlogged. We have
5784 * to do this here so that buffers for the new relfilenumber will
5785 * have the right persistence set, and at the same time ensure
5786 * that the original filenumbers's buffers will get read in with
5787 * the correct setting (i.e. the original one). Otherwise a
5788 * rollback after the rewrite would possibly result with buffers
5789 * for the original filenumbers having the wrong persistence
5790 * setting.
5792 * NB: This relies on swap_relation_files() also swapping the
5793 * persistence. That wouldn't work for pg_class, but that can't be
5794 * unlogged anyway.
5796 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5797 persistence, lockmode);
5800 * Copy the heap data into the new table with the desired
5801 * modifications, and test the current data within the table
5802 * against new constraints generated by ALTER TABLE commands.
5804 ATRewriteTable(tab, OIDNewHeap, lockmode);
5807 * Swap the physical files of the old and new heaps, then rebuild
5808 * indexes and discard the old heap. We can use RecentXmin for
5809 * the table's new relfrozenxid because we rewrote all the tuples
5810 * in ATRewriteTable, so no older Xid remains in the table. Also,
5811 * we never try to swap toast tables by content, since we have no
5812 * interest in letting this code work on system catalogs.
5814 finish_heap_swap(tab->relid, OIDNewHeap,
5815 false, false, true,
5816 !OidIsValid(tab->newTableSpace),
5817 RecentXmin,
5818 ReadNextMultiXactId(),
5819 persistence);
5821 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5823 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5825 if (tab->chgPersistence)
5826 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5828 else
5831 * If required, test the current data within the table against new
5832 * constraints generated by ALTER TABLE commands, but don't
5833 * rebuild data.
5835 if (tab->constraints != NIL || tab->verify_new_notnull ||
5836 tab->partition_constraint != NULL)
5837 ATRewriteTable(tab, InvalidOid, lockmode);
5840 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5841 * just do a block-by-block copy.
5843 if (tab->newTableSpace)
5844 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5848 * Also change persistence of owned sequences, so that it matches the
5849 * table persistence.
5851 if (tab->chgPersistence)
5853 List *seqlist = getOwnedSequences(tab->relid);
5854 ListCell *lc;
5856 foreach(lc, seqlist)
5858 Oid seq_relid = lfirst_oid(lc);
5860 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5866 * Foreign key constraints are checked in a final pass, since (a) it's
5867 * generally best to examine each one separately, and (b) it's at least
5868 * theoretically possible that we have changed both relations of the
5869 * foreign key, and we'd better have finished both rewrites before we try
5870 * to read the tables.
5872 foreach(ltab, *wqueue)
5874 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5875 Relation rel = NULL;
5876 ListCell *lcon;
5878 /* Relations without storage may be ignored here too */
5879 if (!RELKIND_HAS_STORAGE(tab->relkind))
5880 continue;
5882 foreach(lcon, tab->constraints)
5884 NewConstraint *con = lfirst(lcon);
5886 if (con->contype == CONSTR_FOREIGN)
5888 Constraint *fkconstraint = (Constraint *) con->qual;
5889 Relation refrel;
5891 if (rel == NULL)
5893 /* Long since locked, no need for another */
5894 rel = table_open(tab->relid, NoLock);
5897 refrel = table_open(con->refrelid, RowShareLock);
5899 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5900 con->refindid,
5901 con->conid);
5904 * No need to mark the constraint row as validated, we did
5905 * that when we inserted the row earlier.
5908 table_close(refrel, NoLock);
5912 if (rel)
5913 table_close(rel, NoLock);
5916 /* Finally, run any afterStmts that were queued up */
5917 foreach(ltab, *wqueue)
5919 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5920 ListCell *lc;
5922 foreach(lc, tab->afterStmts)
5924 Node *stmt = (Node *) lfirst(lc);
5926 ProcessUtilityForAlterTable(stmt, context);
5927 CommandCounterIncrement();
5933 * ATRewriteTable: scan or rewrite one table
5935 * OIDNewHeap is InvalidOid if we don't need to rewrite
5937 static void
5938 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5940 Relation oldrel;
5941 Relation newrel;
5942 TupleDesc oldTupDesc;
5943 TupleDesc newTupDesc;
5944 bool needscan = false;
5945 List *notnull_attrs;
5946 int i;
5947 ListCell *l;
5948 EState *estate;
5949 CommandId mycid;
5950 BulkInsertState bistate;
5951 int ti_options;
5952 ExprState *partqualstate = NULL;
5955 * Open the relation(s). We have surely already locked the existing
5956 * table.
5958 oldrel = table_open(tab->relid, NoLock);
5959 oldTupDesc = tab->oldDesc;
5960 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
5962 if (OidIsValid(OIDNewHeap))
5963 newrel = table_open(OIDNewHeap, lockmode);
5964 else
5965 newrel = NULL;
5968 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5969 * is empty, so don't bother using it.
5971 if (newrel)
5973 mycid = GetCurrentCommandId(true);
5974 bistate = GetBulkInsertState();
5975 ti_options = TABLE_INSERT_SKIP_FSM;
5977 else
5979 /* keep compiler quiet about using these uninitialized */
5980 mycid = 0;
5981 bistate = NULL;
5982 ti_options = 0;
5986 * Generate the constraint and default execution states
5989 estate = CreateExecutorState();
5991 /* Build the needed expression execution states */
5992 foreach(l, tab->constraints)
5994 NewConstraint *con = lfirst(l);
5996 switch (con->contype)
5998 case CONSTR_CHECK:
5999 needscan = true;
6000 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6001 break;
6002 case CONSTR_FOREIGN:
6003 /* Nothing to do here */
6004 break;
6005 default:
6006 elog(ERROR, "unrecognized constraint type: %d",
6007 (int) con->contype);
6011 /* Build expression execution states for partition check quals */
6012 if (tab->partition_constraint)
6014 needscan = true;
6015 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6018 foreach(l, tab->newvals)
6020 NewColumnValue *ex = lfirst(l);
6022 /* expr already planned */
6023 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6026 notnull_attrs = NIL;
6027 if (newrel || tab->verify_new_notnull)
6030 * If we are rebuilding the tuples OR if we added any new but not
6031 * verified not-null constraints, check all not-null constraints. This
6032 * is a bit of overkill but it minimizes risk of bugs, and
6033 * heap_attisnull is a pretty cheap test anyway.
6035 for (i = 0; i < newTupDesc->natts; i++)
6037 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6039 if (attr->attnotnull && !attr->attisdropped)
6040 notnull_attrs = lappend_int(notnull_attrs, i);
6042 if (notnull_attrs)
6043 needscan = true;
6046 if (newrel || needscan)
6048 ExprContext *econtext;
6049 TupleTableSlot *oldslot;
6050 TupleTableSlot *newslot;
6051 TableScanDesc scan;
6052 MemoryContext oldCxt;
6053 List *dropped_attrs = NIL;
6054 ListCell *lc;
6055 Snapshot snapshot;
6057 if (newrel)
6058 ereport(DEBUG1,
6059 (errmsg_internal("rewriting table \"%s\"",
6060 RelationGetRelationName(oldrel))));
6061 else
6062 ereport(DEBUG1,
6063 (errmsg_internal("verifying table \"%s\"",
6064 RelationGetRelationName(oldrel))));
6066 if (newrel)
6069 * All predicate locks on the tuples or pages are about to be made
6070 * invalid, because we move tuples around. Promote them to
6071 * relation locks.
6073 TransferPredicateLocksToHeapRelation(oldrel);
6076 econtext = GetPerTupleExprContext(estate);
6079 * Create necessary tuple slots. When rewriting, two slots are needed,
6080 * otherwise one suffices. In the case where one slot suffices, we
6081 * need to use the new tuple descriptor, otherwise some constraints
6082 * can't be evaluated. Note that even when the tuple layout is the
6083 * same and no rewrite is required, the tupDescs might not be
6084 * (consider ADD COLUMN without a default).
6086 if (tab->rewrite)
6088 Assert(newrel != NULL);
6089 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6090 table_slot_callbacks(oldrel));
6091 newslot = MakeSingleTupleTableSlot(newTupDesc,
6092 table_slot_callbacks(newrel));
6095 * Set all columns in the new slot to NULL initially, to ensure
6096 * columns added as part of the rewrite are initialized to NULL.
6097 * That is necessary as tab->newvals will not contain an
6098 * expression for columns with a NULL default, e.g. when adding a
6099 * column without a default together with a column with a default
6100 * requiring an actual rewrite.
6102 ExecStoreAllNullTuple(newslot);
6104 else
6106 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6107 table_slot_callbacks(oldrel));
6108 newslot = NULL;
6112 * Any attributes that are dropped according to the new tuple
6113 * descriptor can be set to NULL. We precompute the list of dropped
6114 * attributes to avoid needing to do so in the per-tuple loop.
6116 for (i = 0; i < newTupDesc->natts; i++)
6118 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6119 dropped_attrs = lappend_int(dropped_attrs, i);
6123 * Scan through the rows, generating a new row if needed and then
6124 * checking all the constraints.
6126 snapshot = RegisterSnapshot(GetLatestSnapshot());
6127 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6130 * Switch to per-tuple memory context and reset it for each tuple
6131 * produced, so we don't leak memory.
6133 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6135 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6137 TupleTableSlot *insertslot;
6139 if (tab->rewrite > 0)
6141 /* Extract data from old tuple */
6142 slot_getallattrs(oldslot);
6143 ExecClearTuple(newslot);
6145 /* copy attributes */
6146 memcpy(newslot->tts_values, oldslot->tts_values,
6147 sizeof(Datum) * oldslot->tts_nvalid);
6148 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6149 sizeof(bool) * oldslot->tts_nvalid);
6151 /* Set dropped attributes to null in new tuple */
6152 foreach(lc, dropped_attrs)
6153 newslot->tts_isnull[lfirst_int(lc)] = true;
6156 * Constraints and GENERATED expressions might reference the
6157 * tableoid column, so fill tts_tableOid with the desired
6158 * value. (We must do this each time, because it gets
6159 * overwritten with newrel's OID during storing.)
6161 newslot->tts_tableOid = RelationGetRelid(oldrel);
6164 * Process supplied expressions to replace selected columns.
6166 * First, evaluate expressions whose inputs come from the old
6167 * tuple.
6169 econtext->ecxt_scantuple = oldslot;
6171 foreach(l, tab->newvals)
6173 NewColumnValue *ex = lfirst(l);
6175 if (ex->is_generated)
6176 continue;
6178 newslot->tts_values[ex->attnum - 1]
6179 = ExecEvalExpr(ex->exprstate,
6180 econtext,
6181 &newslot->tts_isnull[ex->attnum - 1]);
6184 ExecStoreVirtualTuple(newslot);
6187 * Now, evaluate any expressions whose inputs come from the
6188 * new tuple. We assume these columns won't reference each
6189 * other, so that there's no ordering dependency.
6191 econtext->ecxt_scantuple = newslot;
6193 foreach(l, tab->newvals)
6195 NewColumnValue *ex = lfirst(l);
6197 if (!ex->is_generated)
6198 continue;
6200 newslot->tts_values[ex->attnum - 1]
6201 = ExecEvalExpr(ex->exprstate,
6202 econtext,
6203 &newslot->tts_isnull[ex->attnum - 1]);
6206 insertslot = newslot;
6208 else
6211 * If there's no rewrite, old and new table are guaranteed to
6212 * have the same AM, so we can just use the old slot to verify
6213 * new constraints etc.
6215 insertslot = oldslot;
6218 /* Now check any constraints on the possibly-changed tuple */
6219 econtext->ecxt_scantuple = insertslot;
6221 foreach(l, notnull_attrs)
6223 int attn = lfirst_int(l);
6225 if (slot_attisnull(insertslot, attn + 1))
6227 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6229 ereport(ERROR,
6230 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6231 errmsg("column \"%s\" of relation \"%s\" contains null values",
6232 NameStr(attr->attname),
6233 RelationGetRelationName(oldrel)),
6234 errtablecol(oldrel, attn + 1)));
6238 foreach(l, tab->constraints)
6240 NewConstraint *con = lfirst(l);
6242 switch (con->contype)
6244 case CONSTR_CHECK:
6245 if (!ExecCheck(con->qualstate, econtext))
6246 ereport(ERROR,
6247 (errcode(ERRCODE_CHECK_VIOLATION),
6248 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6249 con->name,
6250 RelationGetRelationName(oldrel)),
6251 errtableconstraint(oldrel, con->name)));
6252 break;
6253 case CONSTR_NOTNULL:
6254 case CONSTR_FOREIGN:
6255 /* Nothing to do here */
6256 break;
6257 default:
6258 elog(ERROR, "unrecognized constraint type: %d",
6259 (int) con->contype);
6263 if (partqualstate && !ExecCheck(partqualstate, econtext))
6265 if (tab->validate_default)
6266 ereport(ERROR,
6267 (errcode(ERRCODE_CHECK_VIOLATION),
6268 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6269 RelationGetRelationName(oldrel)),
6270 errtable(oldrel)));
6271 else
6272 ereport(ERROR,
6273 (errcode(ERRCODE_CHECK_VIOLATION),
6274 errmsg("partition constraint of relation \"%s\" is violated by some row",
6275 RelationGetRelationName(oldrel)),
6276 errtable(oldrel)));
6279 /* Write the tuple out to the new relation */
6280 if (newrel)
6281 table_tuple_insert(newrel, insertslot, mycid,
6282 ti_options, bistate);
6284 ResetExprContext(econtext);
6286 CHECK_FOR_INTERRUPTS();
6289 MemoryContextSwitchTo(oldCxt);
6290 table_endscan(scan);
6291 UnregisterSnapshot(snapshot);
6293 ExecDropSingleTupleTableSlot(oldslot);
6294 if (newslot)
6295 ExecDropSingleTupleTableSlot(newslot);
6298 FreeExecutorState(estate);
6300 table_close(oldrel, NoLock);
6301 if (newrel)
6303 FreeBulkInsertState(bistate);
6305 table_finish_bulk_insert(newrel, ti_options);
6307 table_close(newrel, NoLock);
6312 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6314 static AlteredTableInfo *
6315 ATGetQueueEntry(List **wqueue, Relation rel)
6317 Oid relid = RelationGetRelid(rel);
6318 AlteredTableInfo *tab;
6319 ListCell *ltab;
6321 foreach(ltab, *wqueue)
6323 tab = (AlteredTableInfo *) lfirst(ltab);
6324 if (tab->relid == relid)
6325 return tab;
6329 * Not there, so add it. Note that we make a copy of the relation's
6330 * existing descriptor before anything interesting can happen to it.
6332 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6333 tab->relid = relid;
6334 tab->rel = NULL; /* set later */
6335 tab->relkind = rel->rd_rel->relkind;
6336 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6337 tab->newAccessMethod = InvalidOid;
6338 tab->newTableSpace = InvalidOid;
6339 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6340 tab->chgPersistence = false;
6342 *wqueue = lappend(*wqueue, tab);
6344 return tab;
6347 static const char *
6348 alter_table_type_to_string(AlterTableType cmdtype)
6350 switch (cmdtype)
6352 case AT_AddColumn:
6353 case AT_AddColumnToView:
6354 return "ADD COLUMN";
6355 case AT_ColumnDefault:
6356 case AT_CookedColumnDefault:
6357 return "ALTER COLUMN ... SET DEFAULT";
6358 case AT_DropNotNull:
6359 return "ALTER COLUMN ... DROP NOT NULL";
6360 case AT_SetNotNull:
6361 return "ALTER COLUMN ... SET NOT NULL";
6362 case AT_SetAttNotNull:
6363 return NULL; /* not real grammar */
6364 case AT_DropExpression:
6365 return "ALTER COLUMN ... DROP EXPRESSION";
6366 case AT_SetStatistics:
6367 return "ALTER COLUMN ... SET STATISTICS";
6368 case AT_SetOptions:
6369 return "ALTER COLUMN ... SET";
6370 case AT_ResetOptions:
6371 return "ALTER COLUMN ... RESET";
6372 case AT_SetStorage:
6373 return "ALTER COLUMN ... SET STORAGE";
6374 case AT_SetCompression:
6375 return "ALTER COLUMN ... SET COMPRESSION";
6376 case AT_DropColumn:
6377 return "DROP COLUMN";
6378 case AT_AddIndex:
6379 case AT_ReAddIndex:
6380 return NULL; /* not real grammar */
6381 case AT_AddConstraint:
6382 case AT_ReAddConstraint:
6383 case AT_ReAddDomainConstraint:
6384 case AT_AddIndexConstraint:
6385 return "ADD CONSTRAINT";
6386 case AT_AlterConstraint:
6387 return "ALTER CONSTRAINT";
6388 case AT_ValidateConstraint:
6389 return "VALIDATE CONSTRAINT";
6390 case AT_DropConstraint:
6391 return "DROP CONSTRAINT";
6392 case AT_ReAddComment:
6393 return NULL; /* not real grammar */
6394 case AT_AlterColumnType:
6395 return "ALTER COLUMN ... SET DATA TYPE";
6396 case AT_AlterColumnGenericOptions:
6397 return "ALTER COLUMN ... OPTIONS";
6398 case AT_ChangeOwner:
6399 return "OWNER TO";
6400 case AT_ClusterOn:
6401 return "CLUSTER ON";
6402 case AT_DropCluster:
6403 return "SET WITHOUT CLUSTER";
6404 case AT_SetAccessMethod:
6405 return "SET ACCESS METHOD";
6406 case AT_SetLogged:
6407 return "SET LOGGED";
6408 case AT_SetUnLogged:
6409 return "SET UNLOGGED";
6410 case AT_DropOids:
6411 return "SET WITHOUT OIDS";
6412 case AT_SetTableSpace:
6413 return "SET TABLESPACE";
6414 case AT_SetRelOptions:
6415 return "SET";
6416 case AT_ResetRelOptions:
6417 return "RESET";
6418 case AT_ReplaceRelOptions:
6419 return NULL; /* not real grammar */
6420 case AT_EnableTrig:
6421 return "ENABLE TRIGGER";
6422 case AT_EnableAlwaysTrig:
6423 return "ENABLE ALWAYS TRIGGER";
6424 case AT_EnableReplicaTrig:
6425 return "ENABLE REPLICA TRIGGER";
6426 case AT_DisableTrig:
6427 return "DISABLE TRIGGER";
6428 case AT_EnableTrigAll:
6429 return "ENABLE TRIGGER ALL";
6430 case AT_DisableTrigAll:
6431 return "DISABLE TRIGGER ALL";
6432 case AT_EnableTrigUser:
6433 return "ENABLE TRIGGER USER";
6434 case AT_DisableTrigUser:
6435 return "DISABLE TRIGGER USER";
6436 case AT_EnableRule:
6437 return "ENABLE RULE";
6438 case AT_EnableAlwaysRule:
6439 return "ENABLE ALWAYS RULE";
6440 case AT_EnableReplicaRule:
6441 return "ENABLE REPLICA RULE";
6442 case AT_DisableRule:
6443 return "DISABLE RULE";
6444 case AT_AddInherit:
6445 return "INHERIT";
6446 case AT_DropInherit:
6447 return "NO INHERIT";
6448 case AT_AddOf:
6449 return "OF";
6450 case AT_DropOf:
6451 return "NOT OF";
6452 case AT_ReplicaIdentity:
6453 return "REPLICA IDENTITY";
6454 case AT_EnableRowSecurity:
6455 return "ENABLE ROW SECURITY";
6456 case AT_DisableRowSecurity:
6457 return "DISABLE ROW SECURITY";
6458 case AT_ForceRowSecurity:
6459 return "FORCE ROW SECURITY";
6460 case AT_NoForceRowSecurity:
6461 return "NO FORCE ROW SECURITY";
6462 case AT_GenericOptions:
6463 return "OPTIONS";
6464 case AT_AttachPartition:
6465 return "ATTACH PARTITION";
6466 case AT_DetachPartition:
6467 return "DETACH PARTITION";
6468 case AT_DetachPartitionFinalize:
6469 return "DETACH PARTITION ... FINALIZE";
6470 case AT_AddIdentity:
6471 return "ALTER COLUMN ... ADD IDENTITY";
6472 case AT_SetIdentity:
6473 return "ALTER COLUMN ... SET";
6474 case AT_DropIdentity:
6475 return "ALTER COLUMN ... DROP IDENTITY";
6476 case AT_ReAddStatistics:
6477 return NULL; /* not real grammar */
6480 return NULL;
6484 * ATSimplePermissions
6486 * - Ensure that it is a relation (or possibly a view)
6487 * - Ensure this user is the owner
6488 * - Ensure that it is not a system table
6490 static void
6491 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6493 int actual_target;
6495 switch (rel->rd_rel->relkind)
6497 case RELKIND_RELATION:
6498 case RELKIND_PARTITIONED_TABLE:
6499 actual_target = ATT_TABLE;
6500 break;
6501 case RELKIND_VIEW:
6502 actual_target = ATT_VIEW;
6503 break;
6504 case RELKIND_MATVIEW:
6505 actual_target = ATT_MATVIEW;
6506 break;
6507 case RELKIND_INDEX:
6508 actual_target = ATT_INDEX;
6509 break;
6510 case RELKIND_PARTITIONED_INDEX:
6511 actual_target = ATT_PARTITIONED_INDEX;
6512 break;
6513 case RELKIND_COMPOSITE_TYPE:
6514 actual_target = ATT_COMPOSITE_TYPE;
6515 break;
6516 case RELKIND_FOREIGN_TABLE:
6517 actual_target = ATT_FOREIGN_TABLE;
6518 break;
6519 case RELKIND_SEQUENCE:
6520 actual_target = ATT_SEQUENCE;
6521 break;
6522 default:
6523 actual_target = 0;
6524 break;
6527 /* Wrong target type? */
6528 if ((actual_target & allowed_targets) == 0)
6530 const char *action_str = alter_table_type_to_string(cmdtype);
6532 if (action_str)
6533 ereport(ERROR,
6534 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6535 /* translator: %s is a group of some SQL keywords */
6536 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6537 action_str, RelationGetRelationName(rel)),
6538 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6539 else
6540 /* internal error? */
6541 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6542 RelationGetRelationName(rel));
6545 /* Permissions checks */
6546 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6547 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6548 RelationGetRelationName(rel));
6550 if (!allowSystemTableMods && IsSystemRelation(rel))
6551 ereport(ERROR,
6552 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6553 errmsg("permission denied: \"%s\" is a system catalog",
6554 RelationGetRelationName(rel))));
6558 * ATSimpleRecursion
6560 * Simple table recursion sufficient for most ALTER TABLE operations.
6561 * All direct and indirect children are processed in an unspecified order.
6562 * Note that if a child inherits from the original table via multiple
6563 * inheritance paths, it will be visited just once.
6565 static void
6566 ATSimpleRecursion(List **wqueue, Relation rel,
6567 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6568 AlterTableUtilityContext *context)
6571 * Propagate to children, if desired and if there are (or might be) any
6572 * children.
6574 if (recurse && rel->rd_rel->relhassubclass)
6576 Oid relid = RelationGetRelid(rel);
6577 ListCell *child;
6578 List *children;
6580 children = find_all_inheritors(relid, lockmode, NULL);
6583 * find_all_inheritors does the recursive search of the inheritance
6584 * hierarchy, so all we have to do is process all of the relids in the
6585 * list that it returns.
6587 foreach(child, children)
6589 Oid childrelid = lfirst_oid(child);
6590 Relation childrel;
6592 if (childrelid == relid)
6593 continue;
6594 /* find_all_inheritors already got lock */
6595 childrel = relation_open(childrelid, NoLock);
6596 CheckTableNotInUse(childrel, "ALTER TABLE");
6597 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6598 relation_close(childrel, NoLock);
6604 * Obtain list of partitions of the given table, locking them all at the given
6605 * lockmode and ensuring that they all pass CheckTableNotInUse.
6607 * This function is a no-op if the given relation is not a partitioned table;
6608 * in particular, nothing is done if it's a legacy inheritance parent.
6610 static void
6611 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6613 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6615 List *inh;
6616 ListCell *cell;
6618 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6619 /* first element is the parent rel; must ignore it */
6620 for_each_from(cell, inh, 1)
6622 Relation childrel;
6624 /* find_all_inheritors already got lock */
6625 childrel = table_open(lfirst_oid(cell), NoLock);
6626 CheckTableNotInUse(childrel, "ALTER TABLE");
6627 table_close(childrel, NoLock);
6629 list_free(inh);
6634 * ATTypedTableRecursion
6636 * Propagate ALTER TYPE operations to the typed tables of that type.
6637 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6638 * recursion to inheritance children of the typed tables.
6640 static void
6641 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6642 LOCKMODE lockmode, AlterTableUtilityContext *context)
6644 ListCell *child;
6645 List *children;
6647 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6649 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6650 RelationGetRelationName(rel),
6651 cmd->behavior);
6653 foreach(child, children)
6655 Oid childrelid = lfirst_oid(child);
6656 Relation childrel;
6658 childrel = relation_open(childrelid, lockmode);
6659 CheckTableNotInUse(childrel, "ALTER TABLE");
6660 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6661 relation_close(childrel, NoLock);
6667 * find_composite_type_dependencies
6669 * Check to see if the type "typeOid" is being used as a column in some table
6670 * (possibly nested several levels deep in composite types, arrays, etc!).
6671 * Eventually, we'd like to propagate the check or rewrite operation
6672 * into such tables, but for now, just error out if we find any.
6674 * Caller should provide either the associated relation of a rowtype,
6675 * or a type name (not both) for use in the error message, if any.
6677 * Note that "typeOid" is not necessarily a composite type; it could also be
6678 * another container type such as an array or range, or a domain over one of
6679 * these things. The name of this function is therefore somewhat historical,
6680 * but it's not worth changing.
6682 * We assume that functions and views depending on the type are not reasons
6683 * to reject the ALTER. (How safe is this really?)
6685 void
6686 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6687 const char *origTypeName)
6689 Relation depRel;
6690 ScanKeyData key[2];
6691 SysScanDesc depScan;
6692 HeapTuple depTup;
6694 /* since this function recurses, it could be driven to stack overflow */
6695 check_stack_depth();
6698 * We scan pg_depend to find those things that depend on the given type.
6699 * (We assume we can ignore refobjsubid for a type.)
6701 depRel = table_open(DependRelationId, AccessShareLock);
6703 ScanKeyInit(&key[0],
6704 Anum_pg_depend_refclassid,
6705 BTEqualStrategyNumber, F_OIDEQ,
6706 ObjectIdGetDatum(TypeRelationId));
6707 ScanKeyInit(&key[1],
6708 Anum_pg_depend_refobjid,
6709 BTEqualStrategyNumber, F_OIDEQ,
6710 ObjectIdGetDatum(typeOid));
6712 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6713 NULL, 2, key);
6715 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6717 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6718 Relation rel;
6719 TupleDesc tupleDesc;
6720 Form_pg_attribute att;
6722 /* Check for directly dependent types */
6723 if (pg_depend->classid == TypeRelationId)
6726 * This must be an array, domain, or range containing the given
6727 * type, so recursively check for uses of this type. Note that
6728 * any error message will mention the original type not the
6729 * container; this is intentional.
6731 find_composite_type_dependencies(pg_depend->objid,
6732 origRelation, origTypeName);
6733 continue;
6736 /* Else, ignore dependees that aren't relations */
6737 if (pg_depend->classid != RelationRelationId)
6738 continue;
6740 rel = relation_open(pg_depend->objid, AccessShareLock);
6741 tupleDesc = RelationGetDescr(rel);
6744 * If objsubid identifies a specific column, refer to that in error
6745 * messages. Otherwise, search to see if there's a user column of the
6746 * type. (We assume system columns are never of interesting types.)
6747 * The search is needed because an index containing an expression
6748 * column of the target type will just be recorded as a whole-relation
6749 * dependency. If we do not find a column of the type, the dependency
6750 * must indicate that the type is transiently referenced in an index
6751 * expression but not stored on disk, which we assume is OK, just as
6752 * we do for references in views. (It could also be that the target
6753 * type is embedded in some container type that is stored in an index
6754 * column, but the previous recursion should catch such cases.)
6756 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6757 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6758 else
6760 att = NULL;
6761 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6763 att = TupleDescAttr(tupleDesc, attno - 1);
6764 if (att->atttypid == typeOid && !att->attisdropped)
6765 break;
6766 att = NULL;
6768 if (att == NULL)
6770 /* No such column, so assume OK */
6771 relation_close(rel, AccessShareLock);
6772 continue;
6777 * We definitely should reject if the relation has storage. If it's
6778 * partitioned, then perhaps we don't have to reject: if there are
6779 * partitions then we'll fail when we find one, else there is no
6780 * stored data to worry about. However, it's possible that the type
6781 * change would affect conclusions about whether the type is sortable
6782 * or hashable and thus (if it's a partitioning column) break the
6783 * partitioning rule. For now, reject for partitioned rels too.
6785 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6786 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6788 if (origTypeName)
6789 ereport(ERROR,
6790 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6791 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6792 origTypeName,
6793 RelationGetRelationName(rel),
6794 NameStr(att->attname))));
6795 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6796 ereport(ERROR,
6797 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6798 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6799 RelationGetRelationName(origRelation),
6800 RelationGetRelationName(rel),
6801 NameStr(att->attname))));
6802 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6803 ereport(ERROR,
6804 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6805 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6806 RelationGetRelationName(origRelation),
6807 RelationGetRelationName(rel),
6808 NameStr(att->attname))));
6809 else
6810 ereport(ERROR,
6811 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6812 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6813 RelationGetRelationName(origRelation),
6814 RelationGetRelationName(rel),
6815 NameStr(att->attname))));
6817 else if (OidIsValid(rel->rd_rel->reltype))
6820 * A view or composite type itself isn't a problem, but we must
6821 * recursively check for indirect dependencies via its rowtype.
6823 find_composite_type_dependencies(rel->rd_rel->reltype,
6824 origRelation, origTypeName);
6827 relation_close(rel, AccessShareLock);
6830 systable_endscan(depScan);
6832 relation_close(depRel, AccessShareLock);
6837 * find_typed_table_dependencies
6839 * Check to see if a composite type is being used as the type of a
6840 * typed table. Abort if any are found and behavior is RESTRICT.
6841 * Else return the list of tables.
6843 static List *
6844 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6846 Relation classRel;
6847 ScanKeyData key[1];
6848 TableScanDesc scan;
6849 HeapTuple tuple;
6850 List *result = NIL;
6852 classRel = table_open(RelationRelationId, AccessShareLock);
6854 ScanKeyInit(&key[0],
6855 Anum_pg_class_reloftype,
6856 BTEqualStrategyNumber, F_OIDEQ,
6857 ObjectIdGetDatum(typeOid));
6859 scan = table_beginscan_catalog(classRel, 1, key);
6861 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6863 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6865 if (behavior == DROP_RESTRICT)
6866 ereport(ERROR,
6867 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6868 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6869 typeName),
6870 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6871 else
6872 result = lappend_oid(result, classform->oid);
6875 table_endscan(scan);
6876 table_close(classRel, AccessShareLock);
6878 return result;
6883 * check_of_type
6885 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6886 * isn't suitable, throw an error. Currently, we require that the type
6887 * originated with CREATE TYPE AS. We could support any row type, but doing so
6888 * would require handling a number of extra corner cases in the DDL commands.
6889 * (Also, allowing domain-over-composite would open up a can of worms about
6890 * whether and how the domain's constraints should apply to derived tables.)
6892 void
6893 check_of_type(HeapTuple typetuple)
6895 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6896 bool typeOk = false;
6898 if (typ->typtype == TYPTYPE_COMPOSITE)
6900 Relation typeRelation;
6902 Assert(OidIsValid(typ->typrelid));
6903 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6904 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6907 * Close the parent rel, but keep our AccessShareLock on it until xact
6908 * commit. That will prevent someone else from deleting or ALTERing
6909 * the type before the typed table creation/conversion commits.
6911 relation_close(typeRelation, NoLock);
6913 if (!typeOk)
6914 ereport(ERROR,
6915 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6916 errmsg("type %s is not a composite type",
6917 format_type_be(typ->oid))));
6922 * ALTER TABLE ADD COLUMN
6924 * Adds an additional attribute to a relation making the assumption that
6925 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6926 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6927 * AlterTableCmd's.
6929 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6930 * have to decide at runtime whether to recurse or not depending on whether we
6931 * actually add a column or merely merge with an existing column. (We can't
6932 * check this in a static pre-pass because it won't handle multiple inheritance
6933 * situations correctly.)
6935 static void
6936 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6937 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6938 AlterTableUtilityContext *context)
6940 if (rel->rd_rel->reloftype && !recursing)
6941 ereport(ERROR,
6942 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6943 errmsg("cannot add column to typed table")));
6945 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6946 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6948 if (recurse && !is_view)
6949 cmd->recurse = true;
6953 * Add a column to a table. The return value is the address of the
6954 * new column in the parent relation.
6956 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6957 * copy (but that happens only after we check for IF NOT EXISTS).
6959 static ObjectAddress
6960 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
6961 AlterTableCmd **cmd, bool recurse, bool recursing,
6962 LOCKMODE lockmode, int cur_pass,
6963 AlterTableUtilityContext *context)
6965 Oid myrelid = RelationGetRelid(rel);
6966 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
6967 bool if_not_exists = (*cmd)->missing_ok;
6968 Relation pgclass,
6969 attrdesc;
6970 HeapTuple reltup;
6971 FormData_pg_attribute attribute;
6972 int newattnum;
6973 char relkind;
6974 HeapTuple typeTuple;
6975 Oid typeOid;
6976 int32 typmod;
6977 Oid collOid;
6978 Form_pg_type tform;
6979 Expr *defval;
6980 List *children;
6981 ListCell *child;
6982 AlterTableCmd *childcmd;
6983 AclResult aclresult;
6984 ObjectAddress address;
6985 TupleDesc tupdesc;
6986 FormData_pg_attribute *aattr[] = {&attribute};
6988 /* At top level, permission check was done in ATPrepCmd, else do it */
6989 if (recursing)
6990 ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6992 if (rel->rd_rel->relispartition && !recursing)
6993 ereport(ERROR,
6994 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6995 errmsg("cannot add column to a partition")));
6997 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7000 * Are we adding the column to a recursion child? If so, check whether to
7001 * merge with an existing definition for the column. If we do merge, we
7002 * must not recurse. Children will already have the column, and recursing
7003 * into them would mess up attinhcount.
7005 if (colDef->inhcount > 0)
7007 HeapTuple tuple;
7009 /* Does child already have a column by this name? */
7010 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7011 if (HeapTupleIsValid(tuple))
7013 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7014 Oid ctypeId;
7015 int32 ctypmod;
7016 Oid ccollid;
7018 /* Child column must match on type, typmod, and collation */
7019 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7020 if (ctypeId != childatt->atttypid ||
7021 ctypmod != childatt->atttypmod)
7022 ereport(ERROR,
7023 (errcode(ERRCODE_DATATYPE_MISMATCH),
7024 errmsg("child table \"%s\" has different type for column \"%s\"",
7025 RelationGetRelationName(rel), colDef->colname)));
7026 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7027 if (ccollid != childatt->attcollation)
7028 ereport(ERROR,
7029 (errcode(ERRCODE_COLLATION_MISMATCH),
7030 errmsg("child table \"%s\" has different collation for column \"%s\"",
7031 RelationGetRelationName(rel), colDef->colname),
7032 errdetail("\"%s\" versus \"%s\"",
7033 get_collation_name(ccollid),
7034 get_collation_name(childatt->attcollation))));
7036 /* Bump the existing child att's inhcount */
7037 childatt->attinhcount++;
7038 if (childatt->attinhcount < 0)
7039 ereport(ERROR,
7040 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7041 errmsg("too many inheritance parents"));
7042 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7044 heap_freetuple(tuple);
7046 /* Inform the user about the merge */
7047 ereport(NOTICE,
7048 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7049 colDef->colname, RelationGetRelationName(rel))));
7051 table_close(attrdesc, RowExclusiveLock);
7052 return InvalidObjectAddress;
7056 /* skip if the name already exists and if_not_exists is true */
7057 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7059 table_close(attrdesc, RowExclusiveLock);
7060 return InvalidObjectAddress;
7064 * Okay, we need to add the column, so go ahead and do parse
7065 * transformation. This can result in queueing up, or even immediately
7066 * executing, subsidiary operations (such as creation of unique indexes);
7067 * so we mustn't do it until we have made the if_not_exists check.
7069 * When recursing, the command was already transformed and we needn't do
7070 * so again. Also, if context isn't given we can't transform. (That
7071 * currently happens only for AT_AddColumnToView; we expect that view.c
7072 * passed us a ColumnDef that doesn't need work.)
7074 if (context != NULL && !recursing)
7076 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7077 cur_pass, context);
7078 Assert(*cmd != NULL);
7079 colDef = castNode(ColumnDef, (*cmd)->def);
7083 * Cannot add identity column if table has children, because identity does
7084 * not inherit. (Adding column and identity separately will work.)
7086 if (colDef->identity &&
7087 recurse &&
7088 find_inheritance_children(myrelid, NoLock) != NIL)
7089 ereport(ERROR,
7090 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7091 errmsg("cannot recursively add identity column to table that has child tables")));
7093 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7095 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7096 if (!HeapTupleIsValid(reltup))
7097 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7098 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7100 /* Determine the new attribute's number */
7101 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7102 if (newattnum > MaxHeapAttributeNumber)
7103 ereport(ERROR,
7104 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7105 errmsg("tables can have at most %d columns",
7106 MaxHeapAttributeNumber)));
7108 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
7109 tform = (Form_pg_type) GETSTRUCT(typeTuple);
7110 typeOid = tform->oid;
7112 aclresult = object_aclcheck(TypeRelationId, typeOid, GetUserId(), ACL_USAGE);
7113 if (aclresult != ACLCHECK_OK)
7114 aclcheck_error_type(aclresult, typeOid);
7116 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
7118 /* make sure datatype is legal for a column */
7119 CheckAttributeType(colDef->colname, typeOid, collOid,
7120 list_make1_oid(rel->rd_rel->reltype),
7124 * Construct new attribute's pg_attribute entry. (Variable-length fields
7125 * are handled by InsertPgAttributeTuples().)
7127 attribute.attrelid = myrelid;
7128 namestrcpy(&(attribute.attname), colDef->colname);
7129 attribute.atttypid = typeOid;
7130 attribute.attstattarget = -1;
7131 attribute.attlen = tform->typlen;
7132 attribute.attnum = newattnum;
7133 if (list_length(colDef->typeName->arrayBounds) > PG_INT16_MAX)
7134 ereport(ERROR,
7135 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7136 errmsg("too many array dimensions"));
7137 attribute.attndims = list_length(colDef->typeName->arrayBounds);
7138 attribute.atttypmod = typmod;
7139 attribute.attbyval = tform->typbyval;
7140 attribute.attalign = tform->typalign;
7141 if (colDef->storage_name)
7142 attribute.attstorage = GetAttributeStorage(typeOid, colDef->storage_name);
7143 else
7144 attribute.attstorage = tform->typstorage;
7145 attribute.attcompression = GetAttributeCompression(typeOid,
7146 colDef->compression);
7147 attribute.attnotnull = colDef->is_not_null;
7148 attribute.atthasdef = false;
7149 attribute.atthasmissing = false;
7150 attribute.attidentity = colDef->identity;
7151 attribute.attgenerated = colDef->generated;
7152 attribute.attisdropped = false;
7153 attribute.attislocal = colDef->is_local;
7154 attribute.attinhcount = colDef->inhcount;
7155 attribute.attcollation = collOid;
7157 ReleaseSysCache(typeTuple);
7159 tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);
7161 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7163 table_close(attrdesc, RowExclusiveLock);
7166 * Update pg_class tuple as appropriate
7168 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7170 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7172 heap_freetuple(reltup);
7174 /* Post creation hook for new attribute */
7175 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7177 table_close(pgclass, RowExclusiveLock);
7179 /* Make the attribute's catalog entry visible */
7180 CommandCounterIncrement();
7183 * Store the DEFAULT, if any, in the catalogs
7185 if (colDef->raw_default)
7187 RawColumnDefault *rawEnt;
7189 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7190 rawEnt->attnum = attribute.attnum;
7191 rawEnt->raw_default = copyObject(colDef->raw_default);
7194 * Attempt to skip a complete table rewrite by storing the specified
7195 * DEFAULT value outside of the heap. This may be disabled inside
7196 * AddRelationNewConstraints if the optimization cannot be applied.
7198 rawEnt->missingMode = (!colDef->generated);
7200 rawEnt->generated = colDef->generated;
7203 * This function is intended for CREATE TABLE, so it processes a
7204 * _list_ of defaults, but we just do one.
7206 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7207 false, true, false, NULL);
7209 /* Make the additional catalog changes visible */
7210 CommandCounterIncrement();
7213 * Did the request for a missing value work? If not we'll have to do a
7214 * rewrite
7216 if (!rawEnt->missingMode)
7217 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7221 * Tell Phase 3 to fill in the default expression, if there is one.
7223 * If there is no default, Phase 3 doesn't have to do anything, because
7224 * that effectively means that the default is NULL. The heap tuple access
7225 * routines always check for attnum > # of attributes in tuple, and return
7226 * NULL if so, so without any modification of the tuple data we will get
7227 * the effect of NULL values in the new column.
7229 * An exception occurs when the new column is of a domain type: the domain
7230 * might have a not-null constraint, or a check constraint that indirectly
7231 * rejects nulls. If there are any domain constraints then we construct
7232 * an explicit NULL default value that will be passed through
7233 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7234 * rewriting the table which we really don't have to do, but the present
7235 * design of domain processing doesn't offer any simple way of checking
7236 * the constraints more directly.)
7238 * Note: we use build_column_default, and not just the cooked default
7239 * returned by AddRelationNewConstraints, so that the right thing happens
7240 * when a datatype's default applies.
7242 * Note: it might seem that this should happen at the end of Phase 2, so
7243 * that the effects of subsequent subcommands can be taken into account.
7244 * It's intentional that we do it now, though. The new column should be
7245 * filled according to what is said in the ADD COLUMN subcommand, so that
7246 * the effects are the same as if this subcommand had been run by itself
7247 * and the later subcommands had been issued in new ALTER TABLE commands.
7249 * We can skip this entirely for relations without storage, since Phase 3
7250 * is certainly not going to touch them. System attributes don't have
7251 * interesting defaults, either.
7253 if (RELKIND_HAS_STORAGE(relkind))
7256 * For an identity column, we can't use build_column_default(),
7257 * because the sequence ownership isn't set yet. So do it manually.
7259 if (colDef->identity)
7261 NextValueExpr *nve = makeNode(NextValueExpr);
7263 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7264 nve->typeId = typeOid;
7266 defval = (Expr *) nve;
7268 /* must do a rewrite for identity columns */
7269 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7271 else
7272 defval = (Expr *) build_column_default(rel, attribute.attnum);
7274 if (!defval && DomainHasConstraints(typeOid))
7276 Oid baseTypeId;
7277 int32 baseTypeMod;
7278 Oid baseTypeColl;
7280 baseTypeMod = typmod;
7281 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
7282 baseTypeColl = get_typcollation(baseTypeId);
7283 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7284 defval = (Expr *) coerce_to_target_type(NULL,
7285 (Node *) defval,
7286 baseTypeId,
7287 typeOid,
7288 typmod,
7289 COERCION_ASSIGNMENT,
7290 COERCE_IMPLICIT_CAST,
7291 -1);
7292 if (defval == NULL) /* should not happen */
7293 elog(ERROR, "failed to coerce base type to domain");
7296 if (defval)
7298 NewColumnValue *newval;
7300 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7301 newval->attnum = attribute.attnum;
7302 newval->expr = expression_planner(defval);
7303 newval->is_generated = (colDef->generated != '\0');
7305 tab->newvals = lappend(tab->newvals, newval);
7308 if (DomainHasConstraints(typeOid))
7309 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7311 if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
7314 * If the new column is NOT NULL, and there is no missing value,
7315 * tell Phase 3 it needs to check for NULLs.
7317 tab->verify_new_notnull |= colDef->is_not_null;
7322 * Add needed dependency entries for the new column.
7324 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
7325 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
7328 * Propagate to children as appropriate. Unlike most other ALTER
7329 * routines, we have to do this one level of recursion at a time; we can't
7330 * use find_all_inheritors to do it in one pass.
7332 children =
7333 find_inheritance_children(RelationGetRelid(rel), lockmode);
7336 * If we are told not to recurse, there had better not be any child
7337 * tables; else the addition would put them out of step.
7339 if (children && !recurse)
7340 ereport(ERROR,
7341 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7342 errmsg("column must be added to child tables too")));
7344 /* Children should see column as singly inherited */
7345 if (!recursing)
7347 childcmd = copyObject(*cmd);
7348 colDef = castNode(ColumnDef, childcmd->def);
7349 colDef->inhcount = 1;
7350 colDef->is_local = false;
7352 else
7353 childcmd = *cmd; /* no need to copy again */
7355 foreach(child, children)
7357 Oid childrelid = lfirst_oid(child);
7358 Relation childrel;
7359 AlteredTableInfo *childtab;
7361 /* find_inheritance_children already got lock */
7362 childrel = table_open(childrelid, NoLock);
7363 CheckTableNotInUse(childrel, "ALTER TABLE");
7365 /* Find or create work queue entry for this table */
7366 childtab = ATGetQueueEntry(wqueue, childrel);
7368 /* Recurse to child; return value is ignored */
7369 ATExecAddColumn(wqueue, childtab, childrel,
7370 &childcmd, recurse, true,
7371 lockmode, cur_pass, context);
7373 table_close(childrel, NoLock);
7376 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7377 return address;
7381 * If a new or renamed column will collide with the name of an existing
7382 * column and if_not_exists is false then error out, else do nothing.
7384 static bool
7385 check_for_column_name_collision(Relation rel, const char *colname,
7386 bool if_not_exists)
7388 HeapTuple attTuple;
7389 int attnum;
7392 * this test is deliberately not attisdropped-aware, since if one tries to
7393 * add a column matching a dropped column name, it's gonna fail anyway.
7395 attTuple = SearchSysCache2(ATTNAME,
7396 ObjectIdGetDatum(RelationGetRelid(rel)),
7397 PointerGetDatum(colname));
7398 if (!HeapTupleIsValid(attTuple))
7399 return true;
7401 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7402 ReleaseSysCache(attTuple);
7405 * We throw a different error message for conflicts with system column
7406 * names, since they are normally not shown and the user might otherwise
7407 * be confused about the reason for the conflict.
7409 if (attnum <= 0)
7410 ereport(ERROR,
7411 (errcode(ERRCODE_DUPLICATE_COLUMN),
7412 errmsg("column name \"%s\" conflicts with a system column name",
7413 colname)));
7414 else
7416 if (if_not_exists)
7418 ereport(NOTICE,
7419 (errcode(ERRCODE_DUPLICATE_COLUMN),
7420 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7421 colname, RelationGetRelationName(rel))));
7422 return false;
7425 ereport(ERROR,
7426 (errcode(ERRCODE_DUPLICATE_COLUMN),
7427 errmsg("column \"%s\" of relation \"%s\" already exists",
7428 colname, RelationGetRelationName(rel))));
7431 return true;
7435 * Install a column's dependency on its datatype.
7437 static void
7438 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7440 ObjectAddress myself,
7441 referenced;
7443 myself.classId = RelationRelationId;
7444 myself.objectId = relid;
7445 myself.objectSubId = attnum;
7446 referenced.classId = TypeRelationId;
7447 referenced.objectId = typid;
7448 referenced.objectSubId = 0;
7449 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7453 * Install a column's dependency on its collation.
7455 static void
7456 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7458 ObjectAddress myself,
7459 referenced;
7461 /* We know the default collation is pinned, so don't bother recording it */
7462 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7464 myself.classId = RelationRelationId;
7465 myself.objectId = relid;
7466 myself.objectSubId = attnum;
7467 referenced.classId = CollationRelationId;
7468 referenced.objectId = collid;
7469 referenced.objectSubId = 0;
7470 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7475 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7477 * Return the address of the modified column. If the column was already
7478 * nullable, InvalidObjectAddress is returned.
7480 static ObjectAddress
7481 ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7482 LOCKMODE lockmode)
7484 HeapTuple tuple;
7485 HeapTuple conTup;
7486 Form_pg_attribute attTup;
7487 AttrNumber attnum;
7488 Relation attr_rel;
7489 ObjectAddress address;
7490 List *readyRels;
7493 * lookup the attribute
7495 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7497 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7498 if (!HeapTupleIsValid(tuple))
7499 ereport(ERROR,
7500 (errcode(ERRCODE_UNDEFINED_COLUMN),
7501 errmsg("column \"%s\" of relation \"%s\" does not exist",
7502 colName, RelationGetRelationName(rel))));
7503 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7504 attnum = attTup->attnum;
7505 ObjectAddressSubSet(address, RelationRelationId,
7506 RelationGetRelid(rel), attnum);
7508 /* If the column is already nullable there's nothing to do. */
7509 if (!attTup->attnotnull)
7511 table_close(attr_rel, RowExclusiveLock);
7512 return InvalidObjectAddress;
7515 /* Prevent them from altering a system attribute */
7516 if (attnum <= 0)
7517 ereport(ERROR,
7518 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7519 errmsg("cannot alter system column \"%s\"",
7520 colName)));
7522 if (attTup->attidentity)
7523 ereport(ERROR,
7524 (errcode(ERRCODE_SYNTAX_ERROR),
7525 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7526 colName, RelationGetRelationName(rel))));
7529 * It's not OK to remove a constraint only for the parent and leave it in
7530 * the children, so disallow that.
7532 if (!recurse)
7534 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7536 PartitionDesc partdesc;
7538 partdesc = RelationGetPartitionDesc(rel, true);
7540 if (partdesc->nparts > 0)
7541 ereport(ERROR,
7542 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7543 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7544 errhint("Do not specify the ONLY keyword."));
7546 else if (rel->rd_rel->relhassubclass &&
7547 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7549 ereport(ERROR,
7550 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7551 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7552 colName),
7553 errhint("Do not specify the ONLY keyword."));
7558 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7560 if (rel->rd_rel->relispartition)
7562 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7563 Relation parent = table_open(parentId, AccessShareLock);
7564 TupleDesc tupDesc = RelationGetDescr(parent);
7565 AttrNumber parent_attnum;
7567 parent_attnum = get_attnum(parentId, colName);
7568 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7569 ereport(ERROR,
7570 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7571 errmsg("column \"%s\" is marked NOT NULL in parent table",
7572 colName)));
7573 table_close(parent, AccessShareLock);
7577 * Find the constraint that makes this column NOT NULL.
7579 conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7580 if (conTup == NULL)
7582 Bitmapset *pkcols;
7585 * There's no not-null constraint, so throw an error. If the column
7586 * is in a primary key, we can throw a specific error. Otherwise,
7587 * this is unexpected.
7589 pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7590 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7591 pkcols))
7592 ereport(ERROR,
7593 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7594 errmsg("column \"%s\" is in a primary key", colName));
7596 /* this shouldn't happen */
7597 elog(ERROR, "could not find not-null constraint on column \"%s\", relation \"%s\"",
7598 colName, RelationGetRelationName(rel));
7601 readyRels = NIL;
7602 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7603 false, &readyRels, lockmode);
7605 heap_freetuple(conTup);
7607 InvokeObjectPostAlterHook(RelationRelationId,
7608 RelationGetRelid(rel), attnum);
7610 table_close(attr_rel, RowExclusiveLock);
7612 return address;
7616 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7617 * to verify it; recurses to apply the same to children.
7619 * When called to alter an existing table, 'wqueue' must be given so that we can
7620 * queue a check that existing tuples pass the constraint. When called from
7621 * table creation, 'wqueue' should be passed as NULL.
7623 * Returns true if the flag was set in any table, otherwise false.
7625 static bool
7626 set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7627 LOCKMODE lockmode)
7629 HeapTuple tuple;
7630 Form_pg_attribute attForm;
7631 bool retval = false;
7633 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7634 if (!HeapTupleIsValid(tuple))
7635 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7636 attnum, RelationGetRelid(rel));
7637 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7638 if (!attForm->attnotnull)
7640 Relation attr_rel;
7642 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7644 attForm->attnotnull = true;
7645 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7647 table_close(attr_rel, RowExclusiveLock);
7650 * And set up for existing values to be checked, unless another
7651 * constraint already proves this.
7653 if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7655 AlteredTableInfo *tab;
7657 tab = ATGetQueueEntry(wqueue, rel);
7658 tab->verify_new_notnull = true;
7661 retval = true;
7664 if (recurse)
7666 List *children;
7667 ListCell *lc;
7669 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7670 foreach(lc, children)
7672 Oid childrelid = lfirst_oid(lc);
7673 Relation childrel;
7674 AttrNumber childattno;
7676 /* find_inheritance_children already got lock */
7677 childrel = table_open(childrelid, NoLock);
7678 CheckTableNotInUse(childrel, "ALTER TABLE");
7680 childattno = get_attnum(RelationGetRelid(childrel),
7681 get_attname(RelationGetRelid(rel), attnum,
7682 false));
7683 retval |= set_attnotnull(wqueue, childrel, childattno,
7684 recurse, lockmode);
7685 table_close(childrel, NoLock);
7689 return retval;
7693 * ALTER TABLE ALTER COLUMN SET NOT NULL
7695 * Add a not-null constraint to a single table and its children. Returns
7696 * the address of the constraint added to the parent relation, if one gets
7697 * added, or InvalidObjectAddress otherwise.
7699 * We must recurse to child tables during execution, rather than using
7700 * ALTER TABLE's normal prep-time recursion.
7702 static ObjectAddress
7703 ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7704 bool recurse, bool recursing, List **readyRels,
7705 LOCKMODE lockmode)
7707 HeapTuple tuple;
7708 Relation constr_rel;
7709 ScanKeyData skey;
7710 SysScanDesc conscan;
7711 AttrNumber attnum;
7712 ObjectAddress address;
7713 Constraint *constraint;
7714 CookedConstraint *ccon;
7715 List *cooked;
7716 bool is_no_inherit = false;
7717 List *ready = NIL;
7720 * In cases of multiple inheritance, we might visit the same child more
7721 * than once. In the topmost call, set up a list that we fill with all
7722 * visited relations, to skip those.
7724 if (readyRels == NULL)
7726 Assert(!recursing);
7727 readyRels = &ready;
7729 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7730 return InvalidObjectAddress;
7731 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7733 /* At top level, permission check was done in ATPrepCmd, else do it */
7734 if (recursing)
7736 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7737 Assert(conName != NULL);
7740 attnum = get_attnum(RelationGetRelid(rel), colName);
7741 if (attnum == InvalidAttrNumber)
7742 ereport(ERROR,
7743 (errcode(ERRCODE_UNDEFINED_COLUMN),
7744 errmsg("column \"%s\" of relation \"%s\" does not exist",
7745 colName, RelationGetRelationName(rel))));
7747 /* Prevent them from altering a system attribute */
7748 if (attnum <= 0)
7749 ereport(ERROR,
7750 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7751 errmsg("cannot alter system column \"%s\"",
7752 colName)));
7754 /* See if there's already a constraint */
7755 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7756 ScanKeyInit(&skey,
7757 Anum_pg_constraint_conrelid,
7758 BTEqualStrategyNumber, F_OIDEQ,
7759 ObjectIdGetDatum(RelationGetRelid(rel)));
7760 conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7761 NULL, 1, &skey);
7763 while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7765 Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7766 bool changed = false;
7767 HeapTuple copytup;
7769 if (conForm->contype != CONSTRAINT_NOTNULL)
7770 continue;
7772 if (extractNotNullColumn(tuple) != attnum)
7773 continue;
7775 copytup = heap_copytuple(tuple);
7776 conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7779 * If we find an appropriate constraint, we're almost done, but just
7780 * need to change some properties on it: if we're recursing, increment
7781 * coninhcount; if not, set conislocal if not already set.
7783 if (recursing)
7785 conForm->coninhcount++;
7786 changed = true;
7788 else if (!conForm->conislocal)
7790 conForm->conislocal = true;
7791 changed = true;
7794 if (changed)
7796 CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
7797 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7800 systable_endscan(conscan);
7801 table_close(constr_rel, RowExclusiveLock);
7803 if (changed)
7804 return address;
7805 else
7806 return InvalidObjectAddress;
7809 systable_endscan(conscan);
7810 table_close(constr_rel, RowExclusiveLock);
7813 * If we're asked not to recurse, and children exist, raise an error for
7814 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7815 * specified.
7817 if (!recurse &&
7818 find_inheritance_children(RelationGetRelid(rel),
7819 NoLock) != NIL)
7821 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7822 ereport(ERROR,
7823 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7824 errmsg("constraint must be added to child tables too"),
7825 errhint("Do not specify the ONLY keyword."));
7826 else
7827 is_no_inherit = true;
7831 * No constraint exists; we must add one. First determine a name to use,
7832 * if we haven't already.
7834 if (!recursing)
7836 Assert(conName == NULL);
7837 conName = ChooseConstraintName(RelationGetRelationName(rel),
7838 colName, "not_null",
7839 RelationGetNamespace(rel),
7840 NIL);
7842 constraint = makeNode(Constraint);
7843 constraint->contype = CONSTR_NOTNULL;
7844 constraint->conname = conName;
7845 constraint->deferrable = false;
7846 constraint->initdeferred = false;
7847 constraint->location = -1;
7848 constraint->keys = list_make1(makeString(colName));
7849 constraint->is_no_inherit = is_no_inherit;
7850 constraint->inhcount = recursing ? 1 : 0;
7851 constraint->skip_validation = false;
7852 constraint->initially_valid = true;
7854 /* and do it */
7855 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7856 false, !recursing, false, NULL);
7857 ccon = linitial(cooked);
7858 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7860 InvokeObjectPostAlterHook(RelationRelationId,
7861 RelationGetRelid(rel), attnum);
7864 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7865 * recurse, because we're going to do it here.
7867 set_attnotnull(wqueue, rel, attnum, false, lockmode);
7870 * Recurse to propagate the constraint to children that don't have one.
7872 if (recurse)
7874 List *children;
7875 ListCell *lc;
7877 children = find_inheritance_children(RelationGetRelid(rel),
7878 lockmode);
7880 foreach(lc, children)
7882 Relation childrel;
7884 childrel = table_open(lfirst_oid(lc), NoLock);
7886 ATExecSetNotNull(wqueue, childrel,
7887 conName, colName, recurse, true,
7888 readyRels, lockmode);
7890 table_close(childrel, NoLock);
7894 return address;
7898 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
7900 * This doesn't exist in the grammar; it's used when creating a
7901 * primary key and the column is not already marked attnotnull.
7903 static ObjectAddress
7904 ATExecSetAttNotNull(List **wqueue, Relation rel,
7905 const char *colName, LOCKMODE lockmode)
7907 AttrNumber attnum;
7908 ObjectAddress address = InvalidObjectAddress;
7910 attnum = get_attnum(RelationGetRelid(rel), colName);
7911 if (attnum == InvalidAttrNumber)
7912 ereport(ERROR,
7913 errcode(ERRCODE_UNDEFINED_COLUMN),
7914 errmsg("column \"%s\" of relation \"%s\" does not exist",
7915 colName, RelationGetRelationName(rel)));
7918 * Make the change, if necessary, and only if so report the column as
7919 * changed
7921 if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
7922 ObjectAddressSubSet(address, RelationRelationId,
7923 RelationGetRelid(rel), attnum);
7925 return address;
7929 * NotNullImpliedByRelConstraints
7930 * Does rel's existing constraints imply NOT NULL for the given attribute?
7932 static bool
7933 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7935 NullTest *nnulltest = makeNode(NullTest);
7937 nnulltest->arg = (Expr *) makeVar(1,
7938 attr->attnum,
7939 attr->atttypid,
7940 attr->atttypmod,
7941 attr->attcollation,
7943 nnulltest->nulltesttype = IS_NOT_NULL;
7946 * argisrow = false is correct even for a composite column, because
7947 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7948 * case, just IS DISTINCT FROM NULL.
7950 nnulltest->argisrow = false;
7951 nnulltest->location = -1;
7953 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7955 ereport(DEBUG1,
7956 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7957 RelationGetRelationName(rel), NameStr(attr->attname))));
7958 return true;
7961 return false;
7965 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7967 * Return the address of the affected column.
7969 static ObjectAddress
7970 ATExecColumnDefault(Relation rel, const char *colName,
7971 Node *newDefault, LOCKMODE lockmode)
7973 TupleDesc tupdesc = RelationGetDescr(rel);
7974 AttrNumber attnum;
7975 ObjectAddress address;
7978 * get the number of the attribute
7980 attnum = get_attnum(RelationGetRelid(rel), colName);
7981 if (attnum == InvalidAttrNumber)
7982 ereport(ERROR,
7983 (errcode(ERRCODE_UNDEFINED_COLUMN),
7984 errmsg("column \"%s\" of relation \"%s\" does not exist",
7985 colName, RelationGetRelationName(rel))));
7987 /* Prevent them from altering a system attribute */
7988 if (attnum <= 0)
7989 ereport(ERROR,
7990 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7991 errmsg("cannot alter system column \"%s\"",
7992 colName)));
7994 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7995 ereport(ERROR,
7996 (errcode(ERRCODE_SYNTAX_ERROR),
7997 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7998 colName, RelationGetRelationName(rel)),
7999 /* translator: %s is an SQL ALTER command */
8000 newDefault ? 0 : errhint("Use %s instead.",
8001 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8003 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8004 ereport(ERROR,
8005 (errcode(ERRCODE_SYNTAX_ERROR),
8006 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8007 colName, RelationGetRelationName(rel)),
8008 newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
8009 /* translator: %s is an SQL ALTER command */
8010 errhint("Use %s instead.",
8011 "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION")));
8014 * Remove any old default for the column. We use RESTRICT here for
8015 * safety, but at present we do not expect anything to depend on the
8016 * default.
8018 * We treat removing the existing default as an internal operation when it
8019 * is preparatory to adding a new default, but as a user-initiated
8020 * operation when the user asked for a drop.
8022 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8023 newDefault != NULL);
8025 if (newDefault)
8027 /* SET DEFAULT */
8028 RawColumnDefault *rawEnt;
8030 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8031 rawEnt->attnum = attnum;
8032 rawEnt->raw_default = newDefault;
8033 rawEnt->missingMode = false;
8034 rawEnt->generated = '\0';
8037 * This function is intended for CREATE TABLE, so it processes a
8038 * _list_ of defaults, but we just do one.
8040 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8041 false, true, false, NULL);
8044 ObjectAddressSubSet(address, RelationRelationId,
8045 RelationGetRelid(rel), attnum);
8046 return address;
8050 * Add a pre-cooked default expression.
8052 * Return the address of the affected column.
8054 static ObjectAddress
8055 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8056 Node *newDefault)
8058 ObjectAddress address;
8060 /* We assume no checking is required */
8063 * Remove any old default for the column. We use RESTRICT here for
8064 * safety, but at present we do not expect anything to depend on the
8065 * default. (In ordinary cases, there could not be a default in place
8066 * anyway, but it's possible when combining LIKE with inheritance.)
8068 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8069 true);
8071 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8073 ObjectAddressSubSet(address, RelationRelationId,
8074 RelationGetRelid(rel), attnum);
8075 return address;
8079 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8081 * Return the address of the affected column.
8083 static ObjectAddress
8084 ATExecAddIdentity(Relation rel, const char *colName,
8085 Node *def, LOCKMODE lockmode)
8087 Relation attrelation;
8088 HeapTuple tuple;
8089 Form_pg_attribute attTup;
8090 AttrNumber attnum;
8091 ObjectAddress address;
8092 ColumnDef *cdef = castNode(ColumnDef, def);
8094 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8096 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8097 if (!HeapTupleIsValid(tuple))
8098 ereport(ERROR,
8099 (errcode(ERRCODE_UNDEFINED_COLUMN),
8100 errmsg("column \"%s\" of relation \"%s\" does not exist",
8101 colName, RelationGetRelationName(rel))));
8102 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8103 attnum = attTup->attnum;
8105 /* Can't alter a system attribute */
8106 if (attnum <= 0)
8107 ereport(ERROR,
8108 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8109 errmsg("cannot alter system column \"%s\"",
8110 colName)));
8113 * Creating a column as identity implies NOT NULL, so adding the identity
8114 * to an existing column that is not NOT NULL would create a state that
8115 * cannot be reproduced without contortions.
8117 if (!attTup->attnotnull)
8118 ereport(ERROR,
8119 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8120 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8121 colName, RelationGetRelationName(rel))));
8123 if (attTup->attidentity)
8124 ereport(ERROR,
8125 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8126 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8127 colName, RelationGetRelationName(rel))));
8129 if (attTup->atthasdef)
8130 ereport(ERROR,
8131 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8132 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8133 colName, RelationGetRelationName(rel))));
8135 attTup->attidentity = cdef->identity;
8136 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8138 InvokeObjectPostAlterHook(RelationRelationId,
8139 RelationGetRelid(rel),
8140 attTup->attnum);
8141 ObjectAddressSubSet(address, RelationRelationId,
8142 RelationGetRelid(rel), attnum);
8143 heap_freetuple(tuple);
8145 table_close(attrelation, RowExclusiveLock);
8147 return address;
8151 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8153 * Return the address of the affected column.
8155 static ObjectAddress
8156 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
8158 ListCell *option;
8159 DefElem *generatedEl = NULL;
8160 HeapTuple tuple;
8161 Form_pg_attribute attTup;
8162 AttrNumber attnum;
8163 Relation attrelation;
8164 ObjectAddress address;
8166 foreach(option, castNode(List, def))
8168 DefElem *defel = lfirst_node(DefElem, option);
8170 if (strcmp(defel->defname, "generated") == 0)
8172 if (generatedEl)
8173 ereport(ERROR,
8174 (errcode(ERRCODE_SYNTAX_ERROR),
8175 errmsg("conflicting or redundant options")));
8176 generatedEl = defel;
8178 else
8179 elog(ERROR, "option \"%s\" not recognized",
8180 defel->defname);
8184 * Even if there is nothing to change here, we run all the checks. There
8185 * will be a subsequent ALTER SEQUENCE that relies on everything being
8186 * there.
8189 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8190 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8191 if (!HeapTupleIsValid(tuple))
8192 ereport(ERROR,
8193 (errcode(ERRCODE_UNDEFINED_COLUMN),
8194 errmsg("column \"%s\" of relation \"%s\" does not exist",
8195 colName, RelationGetRelationName(rel))));
8197 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8198 attnum = attTup->attnum;
8200 if (attnum <= 0)
8201 ereport(ERROR,
8202 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8203 errmsg("cannot alter system column \"%s\"",
8204 colName)));
8206 if (!attTup->attidentity)
8207 ereport(ERROR,
8208 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8209 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8210 colName, RelationGetRelationName(rel))));
8212 if (generatedEl)
8214 attTup->attidentity = defGetInt32(generatedEl);
8215 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8217 InvokeObjectPostAlterHook(RelationRelationId,
8218 RelationGetRelid(rel),
8219 attTup->attnum);
8220 ObjectAddressSubSet(address, RelationRelationId,
8221 RelationGetRelid(rel), attnum);
8223 else
8224 address = InvalidObjectAddress;
8226 heap_freetuple(tuple);
8227 table_close(attrelation, RowExclusiveLock);
8229 return address;
8233 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8235 * Return the address of the affected column.
8237 static ObjectAddress
8238 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8240 HeapTuple tuple;
8241 Form_pg_attribute attTup;
8242 AttrNumber attnum;
8243 Relation attrelation;
8244 ObjectAddress address;
8245 Oid seqid;
8246 ObjectAddress seqaddress;
8248 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8249 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8250 if (!HeapTupleIsValid(tuple))
8251 ereport(ERROR,
8252 (errcode(ERRCODE_UNDEFINED_COLUMN),
8253 errmsg("column \"%s\" of relation \"%s\" does not exist",
8254 colName, RelationGetRelationName(rel))));
8256 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8257 attnum = attTup->attnum;
8259 if (attnum <= 0)
8260 ereport(ERROR,
8261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8262 errmsg("cannot alter system column \"%s\"",
8263 colName)));
8265 if (!attTup->attidentity)
8267 if (!missing_ok)
8268 ereport(ERROR,
8269 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8270 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8271 colName, RelationGetRelationName(rel))));
8272 else
8274 ereport(NOTICE,
8275 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8276 colName, RelationGetRelationName(rel))));
8277 heap_freetuple(tuple);
8278 table_close(attrelation, RowExclusiveLock);
8279 return InvalidObjectAddress;
8283 attTup->attidentity = '\0';
8284 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8286 InvokeObjectPostAlterHook(RelationRelationId,
8287 RelationGetRelid(rel),
8288 attTup->attnum);
8289 ObjectAddressSubSet(address, RelationRelationId,
8290 RelationGetRelid(rel), attnum);
8291 heap_freetuple(tuple);
8293 table_close(attrelation, RowExclusiveLock);
8295 /* drop the internal sequence */
8296 seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
8297 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8298 RelationRelationId, DEPENDENCY_INTERNAL);
8299 CommandCounterIncrement();
8300 seqaddress.classId = RelationRelationId;
8301 seqaddress.objectId = seqid;
8302 seqaddress.objectSubId = 0;
8303 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8305 return address;
8309 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8311 static void
8312 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8315 * Reject ONLY if there are child tables. We could implement this, but it
8316 * is a bit complicated. GENERATED clauses must be attached to the column
8317 * definition and cannot be added later like DEFAULT, so if a child table
8318 * has a generation expression that the parent does not have, the child
8319 * column will necessarily be an attislocal column. So to implement ONLY
8320 * here, we'd need extra code to update attislocal of the direct child
8321 * tables, somewhat similar to how DROP COLUMN does it, so that the
8322 * resulting state can be properly dumped and restored.
8324 if (!recurse &&
8325 find_inheritance_children(RelationGetRelid(rel), lockmode))
8326 ereport(ERROR,
8327 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8328 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8331 * Cannot drop generation expression from inherited columns.
8333 if (!recursing)
8335 HeapTuple tuple;
8336 Form_pg_attribute attTup;
8338 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8339 if (!HeapTupleIsValid(tuple))
8340 ereport(ERROR,
8341 (errcode(ERRCODE_UNDEFINED_COLUMN),
8342 errmsg("column \"%s\" of relation \"%s\" does not exist",
8343 cmd->name, RelationGetRelationName(rel))));
8345 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8347 if (attTup->attinhcount > 0)
8348 ereport(ERROR,
8349 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8350 errmsg("cannot drop generation expression from inherited column")));
8355 * Return the address of the affected column.
8357 static ObjectAddress
8358 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8360 HeapTuple tuple;
8361 Form_pg_attribute attTup;
8362 AttrNumber attnum;
8363 Relation attrelation;
8364 Oid attrdefoid;
8365 ObjectAddress address;
8367 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8368 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8369 if (!HeapTupleIsValid(tuple))
8370 ereport(ERROR,
8371 (errcode(ERRCODE_UNDEFINED_COLUMN),
8372 errmsg("column \"%s\" of relation \"%s\" does not exist",
8373 colName, RelationGetRelationName(rel))));
8375 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8376 attnum = attTup->attnum;
8378 if (attnum <= 0)
8379 ereport(ERROR,
8380 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8381 errmsg("cannot alter system column \"%s\"",
8382 colName)));
8384 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8386 if (!missing_ok)
8387 ereport(ERROR,
8388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8389 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8390 colName, RelationGetRelationName(rel))));
8391 else
8393 ereport(NOTICE,
8394 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8395 colName, RelationGetRelationName(rel))));
8396 heap_freetuple(tuple);
8397 table_close(attrelation, RowExclusiveLock);
8398 return InvalidObjectAddress;
8403 * Mark the column as no longer generated. (The atthasdef flag needs to
8404 * get cleared too, but RemoveAttrDefault will handle that.)
8406 attTup->attgenerated = '\0';
8407 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8409 InvokeObjectPostAlterHook(RelationRelationId,
8410 RelationGetRelid(rel),
8411 attnum);
8412 heap_freetuple(tuple);
8414 table_close(attrelation, RowExclusiveLock);
8417 * Drop the dependency records of the GENERATED expression, in particular
8418 * its INTERNAL dependency on the column, which would otherwise cause
8419 * dependency.c to refuse to perform the deletion.
8421 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8422 if (!OidIsValid(attrdefoid))
8423 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8424 RelationGetRelid(rel), attnum);
8425 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8427 /* Make above changes visible */
8428 CommandCounterIncrement();
8431 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8432 * safety, but at present we do not expect anything to depend on the
8433 * default.
8435 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8436 false, false);
8438 ObjectAddressSubSet(address, RelationRelationId,
8439 RelationGetRelid(rel), attnum);
8440 return address;
8444 * ALTER TABLE ALTER COLUMN SET STATISTICS
8446 * Return value is the address of the modified column
8448 static ObjectAddress
8449 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8451 int newtarget;
8452 Relation attrelation;
8453 HeapTuple tuple;
8454 Form_pg_attribute attrtuple;
8455 AttrNumber attnum;
8456 ObjectAddress address;
8459 * We allow referencing columns by numbers only for indexes, since table
8460 * column numbers could contain gaps if columns are later dropped.
8462 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8463 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8464 !colName)
8465 ereport(ERROR,
8466 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8467 errmsg("cannot refer to non-index column by number")));
8469 Assert(IsA(newValue, Integer));
8470 newtarget = intVal(newValue);
8473 * Limit target to a sane range
8475 if (newtarget < -1)
8477 ereport(ERROR,
8478 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8479 errmsg("statistics target %d is too low",
8480 newtarget)));
8482 else if (newtarget > MAX_STATISTICS_TARGET)
8484 newtarget = MAX_STATISTICS_TARGET;
8485 ereport(WARNING,
8486 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8487 errmsg("lowering statistics target to %d",
8488 newtarget)));
8491 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8493 if (colName)
8495 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8497 if (!HeapTupleIsValid(tuple))
8498 ereport(ERROR,
8499 (errcode(ERRCODE_UNDEFINED_COLUMN),
8500 errmsg("column \"%s\" of relation \"%s\" does not exist",
8501 colName, RelationGetRelationName(rel))));
8503 else
8505 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
8507 if (!HeapTupleIsValid(tuple))
8508 ereport(ERROR,
8509 (errcode(ERRCODE_UNDEFINED_COLUMN),
8510 errmsg("column number %d of relation \"%s\" does not exist",
8511 colNum, RelationGetRelationName(rel))));
8514 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8516 attnum = attrtuple->attnum;
8517 if (attnum <= 0)
8518 ereport(ERROR,
8519 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8520 errmsg("cannot alter system column \"%s\"",
8521 colName)));
8523 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8524 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8526 if (attnum > rel->rd_index->indnkeyatts)
8527 ereport(ERROR,
8528 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8529 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8530 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8531 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8532 ereport(ERROR,
8533 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8534 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8535 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8536 errhint("Alter statistics on table column instead.")));
8539 attrtuple->attstattarget = newtarget;
8541 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8543 InvokeObjectPostAlterHook(RelationRelationId,
8544 RelationGetRelid(rel),
8545 attrtuple->attnum);
8546 ObjectAddressSubSet(address, RelationRelationId,
8547 RelationGetRelid(rel), attnum);
8548 heap_freetuple(tuple);
8550 table_close(attrelation, RowExclusiveLock);
8552 return address;
8556 * Return value is the address of the modified column
8558 static ObjectAddress
8559 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8560 bool isReset, LOCKMODE lockmode)
8562 Relation attrelation;
8563 HeapTuple tuple,
8564 newtuple;
8565 Form_pg_attribute attrtuple;
8566 AttrNumber attnum;
8567 Datum datum,
8568 newOptions;
8569 bool isnull;
8570 ObjectAddress address;
8571 Datum repl_val[Natts_pg_attribute];
8572 bool repl_null[Natts_pg_attribute];
8573 bool repl_repl[Natts_pg_attribute];
8575 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8577 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8579 if (!HeapTupleIsValid(tuple))
8580 ereport(ERROR,
8581 (errcode(ERRCODE_UNDEFINED_COLUMN),
8582 errmsg("column \"%s\" of relation \"%s\" does not exist",
8583 colName, RelationGetRelationName(rel))));
8584 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8586 attnum = attrtuple->attnum;
8587 if (attnum <= 0)
8588 ereport(ERROR,
8589 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8590 errmsg("cannot alter system column \"%s\"",
8591 colName)));
8593 /* Generate new proposed attoptions (text array) */
8594 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8595 &isnull);
8596 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8597 castNode(List, options), NULL, NULL,
8598 false, isReset);
8599 /* Validate new options */
8600 (void) attribute_reloptions(newOptions, true);
8602 /* Build new tuple. */
8603 memset(repl_null, false, sizeof(repl_null));
8604 memset(repl_repl, false, sizeof(repl_repl));
8605 if (newOptions != (Datum) 0)
8606 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8607 else
8608 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8609 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8610 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8611 repl_val, repl_null, repl_repl);
8613 /* Update system catalog. */
8614 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8616 InvokeObjectPostAlterHook(RelationRelationId,
8617 RelationGetRelid(rel),
8618 attrtuple->attnum);
8619 ObjectAddressSubSet(address, RelationRelationId,
8620 RelationGetRelid(rel), attnum);
8622 heap_freetuple(newtuple);
8624 ReleaseSysCache(tuple);
8626 table_close(attrelation, RowExclusiveLock);
8628 return address;
8632 * Helper function for ATExecSetStorage and ATExecSetCompression
8634 * Set the attstorage and/or attcompression fields for index columns
8635 * associated with the specified table column.
8637 static void
8638 SetIndexStorageProperties(Relation rel, Relation attrelation,
8639 AttrNumber attnum,
8640 bool setstorage, char newstorage,
8641 bool setcompression, char newcompression,
8642 LOCKMODE lockmode)
8644 ListCell *lc;
8646 foreach(lc, RelationGetIndexList(rel))
8648 Oid indexoid = lfirst_oid(lc);
8649 Relation indrel;
8650 AttrNumber indattnum = 0;
8651 HeapTuple tuple;
8653 indrel = index_open(indexoid, lockmode);
8655 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8657 if (indrel->rd_index->indkey.values[i] == attnum)
8659 indattnum = i + 1;
8660 break;
8664 if (indattnum == 0)
8666 index_close(indrel, lockmode);
8667 continue;
8670 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8672 if (HeapTupleIsValid(tuple))
8674 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8676 if (setstorage)
8677 attrtuple->attstorage = newstorage;
8679 if (setcompression)
8680 attrtuple->attcompression = newcompression;
8682 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8684 InvokeObjectPostAlterHook(RelationRelationId,
8685 RelationGetRelid(rel),
8686 attrtuple->attnum);
8688 heap_freetuple(tuple);
8691 index_close(indrel, lockmode);
8696 * ALTER TABLE ALTER COLUMN SET STORAGE
8698 * Return value is the address of the modified column
8700 static ObjectAddress
8701 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8703 Relation attrelation;
8704 HeapTuple tuple;
8705 Form_pg_attribute attrtuple;
8706 AttrNumber attnum;
8707 ObjectAddress address;
8709 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8711 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8713 if (!HeapTupleIsValid(tuple))
8714 ereport(ERROR,
8715 (errcode(ERRCODE_UNDEFINED_COLUMN),
8716 errmsg("column \"%s\" of relation \"%s\" does not exist",
8717 colName, RelationGetRelationName(rel))));
8718 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8720 attnum = attrtuple->attnum;
8721 if (attnum <= 0)
8722 ereport(ERROR,
8723 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8724 errmsg("cannot alter system column \"%s\"",
8725 colName)));
8727 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8729 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8731 InvokeObjectPostAlterHook(RelationRelationId,
8732 RelationGetRelid(rel),
8733 attrtuple->attnum);
8736 * Apply the change to indexes as well (only for simple index columns,
8737 * matching behavior of index.c ConstructTupleDescriptor()).
8739 SetIndexStorageProperties(rel, attrelation, attnum,
8740 true, attrtuple->attstorage,
8741 false, 0,
8742 lockmode);
8744 heap_freetuple(tuple);
8746 table_close(attrelation, RowExclusiveLock);
8748 ObjectAddressSubSet(address, RelationRelationId,
8749 RelationGetRelid(rel), attnum);
8750 return address;
8755 * ALTER TABLE DROP COLUMN
8757 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8758 * because we have to decide at runtime whether to recurse or not depending
8759 * on whether attinhcount goes to zero or not. (We can't check this in a
8760 * static pre-pass because it won't handle multiple inheritance situations
8761 * correctly.)
8763 static void
8764 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8765 AlterTableCmd *cmd, LOCKMODE lockmode,
8766 AlterTableUtilityContext *context)
8768 if (rel->rd_rel->reloftype && !recursing)
8769 ereport(ERROR,
8770 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8771 errmsg("cannot drop column from typed table")));
8773 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8774 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8776 if (recurse)
8777 cmd->recurse = true;
8781 * Drops column 'colName' from relation 'rel' and returns the address of the
8782 * dropped column. The column is also dropped (or marked as no longer
8783 * inherited from relation) from the relation's inheritance children, if any.
8785 * In the recursive invocations for inheritance child relations, instead of
8786 * dropping the column directly (if to be dropped at all), its object address
8787 * is added to 'addrs', which must be non-NULL in such invocations. All
8788 * columns are dropped at the same time after all the children have been
8789 * checked recursively.
8791 static ObjectAddress
8792 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8793 DropBehavior behavior,
8794 bool recurse, bool recursing,
8795 bool missing_ok, LOCKMODE lockmode,
8796 ObjectAddresses *addrs)
8798 HeapTuple tuple;
8799 Form_pg_attribute targetatt;
8800 AttrNumber attnum;
8801 List *children;
8802 ObjectAddress object;
8803 bool is_expr;
8805 /* At top level, permission check was done in ATPrepCmd, else do it */
8806 if (recursing)
8807 ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8809 /* Initialize addrs on the first invocation */
8810 Assert(!recursing || addrs != NULL);
8811 if (!recursing)
8812 addrs = new_object_addresses();
8815 * get the number of the attribute
8817 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8818 if (!HeapTupleIsValid(tuple))
8820 if (!missing_ok)
8822 ereport(ERROR,
8823 (errcode(ERRCODE_UNDEFINED_COLUMN),
8824 errmsg("column \"%s\" of relation \"%s\" does not exist",
8825 colName, RelationGetRelationName(rel))));
8827 else
8829 ereport(NOTICE,
8830 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8831 colName, RelationGetRelationName(rel))));
8832 return InvalidObjectAddress;
8835 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
8837 attnum = targetatt->attnum;
8839 /* Can't drop a system attribute */
8840 if (attnum <= 0)
8841 ereport(ERROR,
8842 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8843 errmsg("cannot drop system column \"%s\"",
8844 colName)));
8847 * Don't drop inherited columns, unless recursing (presumably from a drop
8848 * of the parent column)
8850 if (targetatt->attinhcount > 0 && !recursing)
8851 ereport(ERROR,
8852 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8853 errmsg("cannot drop inherited column \"%s\"",
8854 colName)));
8857 * Don't drop columns used in the partition key, either. (If we let this
8858 * go through, the key column's dependencies would cause a cascaded drop
8859 * of the whole table, which is surely not what the user expected.)
8861 if (has_partition_attrs(rel,
8862 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
8863 &is_expr))
8864 ereport(ERROR,
8865 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8866 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8867 colName, RelationGetRelationName(rel))));
8869 ReleaseSysCache(tuple);
8872 * Propagate to children as appropriate. Unlike most other ALTER
8873 * routines, we have to do this one level of recursion at a time; we can't
8874 * use find_all_inheritors to do it in one pass.
8876 children =
8877 find_inheritance_children(RelationGetRelid(rel), lockmode);
8879 if (children)
8881 Relation attr_rel;
8882 ListCell *child;
8885 * In case of a partitioned table, the column must be dropped from the
8886 * partitions as well.
8888 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
8889 ereport(ERROR,
8890 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8891 errmsg("cannot drop column from only the partitioned table when partitions exist"),
8892 errhint("Do not specify the ONLY keyword.")));
8894 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
8895 foreach(child, children)
8897 Oid childrelid = lfirst_oid(child);
8898 Relation childrel;
8899 Form_pg_attribute childatt;
8901 /* find_inheritance_children already got lock */
8902 childrel = table_open(childrelid, NoLock);
8903 CheckTableNotInUse(childrel, "ALTER TABLE");
8905 tuple = SearchSysCacheCopyAttName(childrelid, colName);
8906 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
8907 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
8908 colName, childrelid);
8909 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
8911 if (childatt->attinhcount <= 0) /* shouldn't happen */
8912 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
8913 childrelid, colName);
8915 if (recurse)
8918 * If the child column has other definition sources, just
8919 * decrement its inheritance count; if not, recurse to delete
8920 * it.
8922 if (childatt->attinhcount == 1 && !childatt->attislocal)
8924 /* Time to delete this child column, too */
8925 ATExecDropColumn(wqueue, childrel, colName,
8926 behavior, true, true,
8927 false, lockmode, addrs);
8929 else
8931 /* Child column must survive my deletion */
8932 childatt->attinhcount--;
8934 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8936 /* Make update visible */
8937 CommandCounterIncrement();
8940 else
8943 * If we were told to drop ONLY in this table (no recursion),
8944 * we need to mark the inheritors' attributes as locally
8945 * defined rather than inherited.
8947 childatt->attinhcount--;
8948 childatt->attislocal = true;
8950 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8952 /* Make update visible */
8953 CommandCounterIncrement();
8956 heap_freetuple(tuple);
8958 table_close(childrel, NoLock);
8960 table_close(attr_rel, RowExclusiveLock);
8963 /* Add object to delete */
8964 object.classId = RelationRelationId;
8965 object.objectId = RelationGetRelid(rel);
8966 object.objectSubId = attnum;
8967 add_exact_object_address(&object, addrs);
8969 if (!recursing)
8971 /* Recursion has ended, drop everything that was collected */
8972 performMultipleDeletions(addrs, behavior, 0);
8973 free_object_addresses(addrs);
8976 return object;
8980 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
8981 * constraint on its children.
8983 static void
8984 ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
8985 LOCKMODE lockmode, AlterTableUtilityContext *context)
8987 List *children;
8988 List *newconstrs = NIL;
8989 ListCell *lc;
8990 IndexStmt *indexstmt;
8992 /* No work if not creating a primary key */
8993 if (!IsA(cmd->def, IndexStmt))
8994 return;
8995 indexstmt = castNode(IndexStmt, cmd->def);
8996 if (!indexstmt->primary)
8997 return;
8999 /* No work if no legacy inheritance children are present */
9000 if (rel->rd_rel->relkind != RELKIND_RELATION ||
9001 !rel->rd_rel->relhassubclass)
9002 return;
9004 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
9006 foreach(lc, indexstmt->indexParams)
9008 IndexElem *elem = lfirst_node(IndexElem, lc);
9009 Constraint *nnconstr;
9011 Assert(elem->expr == NULL);
9013 nnconstr = makeNode(Constraint);
9014 nnconstr->contype = CONSTR_NOTNULL;
9015 nnconstr->conname = NULL; /* XXX use PK name? */
9016 nnconstr->inhcount = 1;
9017 nnconstr->deferrable = false;
9018 nnconstr->initdeferred = false;
9019 nnconstr->location = -1;
9020 nnconstr->keys = list_make1(makeString(elem->name));
9021 nnconstr->skip_validation = false;
9022 nnconstr->initially_valid = true;
9024 newconstrs = lappend(newconstrs, nnconstr);
9027 foreach(lc, children)
9029 Oid childrelid = lfirst_oid(lc);
9030 Relation childrel = table_open(childrelid, NoLock);
9031 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
9032 ListCell *lc2;
9034 newcmd->subtype = AT_AddConstraint;
9035 newcmd->recurse = true;
9037 foreach(lc2, newconstrs)
9039 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9040 newcmd->def = lfirst(lc2);
9042 ATPrepCmd(wqueue, childrel, newcmd,
9043 true, false, lockmode, context);
9046 table_close(childrel, NoLock);
9051 * ALTER TABLE ADD INDEX
9053 * There is no such command in the grammar, but parse_utilcmd.c converts
9054 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9055 * us schedule creation of the index at the appropriate time during ALTER.
9057 * Return value is the address of the new index.
9059 static ObjectAddress
9060 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9061 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9063 bool check_rights;
9064 bool skip_build;
9065 bool quiet;
9066 ObjectAddress address;
9068 Assert(IsA(stmt, IndexStmt));
9069 Assert(!stmt->concurrent);
9071 /* The IndexStmt has already been through transformIndexStmt */
9072 Assert(stmt->transformed);
9074 /* suppress schema rights check when rebuilding existing index */
9075 check_rights = !is_rebuild;
9076 /* skip index build if phase 3 will do it or we're reusing an old one */
9077 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9078 /* suppress notices when rebuilding existing index */
9079 quiet = is_rebuild;
9081 address = DefineIndex(RelationGetRelid(rel),
9082 stmt,
9083 InvalidOid, /* no predefined OID */
9084 InvalidOid, /* no parent index */
9085 InvalidOid, /* no parent constraint */
9086 -1, /* total_parts unknown */
9087 true, /* is_alter_table */
9088 check_rights,
9089 false, /* check_not_in_use - we did it already */
9090 skip_build,
9091 quiet);
9094 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9095 * new index instead of building from scratch. Restore associated fields.
9096 * This may store InvalidSubTransactionId in both fields, in which case
9097 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9098 * this after the CCI that made catalog rows visible to any rebuild. The
9099 * DROP of the old edition of this index will have scheduled the storage
9100 * for deletion at commit, so cancel that pending deletion.
9102 if (RelFileNumberIsValid(stmt->oldNumber))
9104 Relation irel = index_open(address.objectId, NoLock);
9106 irel->rd_createSubid = stmt->oldCreateSubid;
9107 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9108 RelationPreserveStorage(irel->rd_locator, true);
9109 index_close(irel, NoLock);
9112 return address;
9116 * ALTER TABLE ADD STATISTICS
9118 * This is no such command in the grammar, but we use this internally to add
9119 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9120 * column type change.
9122 static ObjectAddress
9123 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9124 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9126 ObjectAddress address;
9128 Assert(IsA(stmt, CreateStatsStmt));
9130 /* The CreateStatsStmt has already been through transformStatsStmt */
9131 Assert(stmt->transformed);
9133 address = CreateStatistics(stmt);
9135 return address;
9139 * ALTER TABLE ADD CONSTRAINT USING INDEX
9141 * Returns the address of the new constraint.
9143 static ObjectAddress
9144 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9145 IndexStmt *stmt, LOCKMODE lockmode)
9147 Oid index_oid = stmt->indexOid;
9148 Relation indexRel;
9149 char *indexName;
9150 IndexInfo *indexInfo;
9151 char *constraintName;
9152 char constraintType;
9153 ObjectAddress address;
9154 bits16 flags;
9156 Assert(IsA(stmt, IndexStmt));
9157 Assert(OidIsValid(index_oid));
9158 Assert(stmt->isconstraint);
9161 * Doing this on partitioned tables is not a simple feature to implement,
9162 * so let's punt for now.
9164 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9165 ereport(ERROR,
9166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9167 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9169 indexRel = index_open(index_oid, AccessShareLock);
9171 indexName = pstrdup(RelationGetRelationName(indexRel));
9173 indexInfo = BuildIndexInfo(indexRel);
9175 /* this should have been checked at parse time */
9176 if (!indexInfo->ii_Unique)
9177 elog(ERROR, "index \"%s\" is not unique", indexName);
9180 * Determine name to assign to constraint. We require a constraint to
9181 * have the same name as the underlying index; therefore, use the index's
9182 * existing name as the default constraint name, and if the user
9183 * explicitly gives some other name for the constraint, rename the index
9184 * to match.
9186 constraintName = stmt->idxname;
9187 if (constraintName == NULL)
9188 constraintName = indexName;
9189 else if (strcmp(constraintName, indexName) != 0)
9191 ereport(NOTICE,
9192 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9193 indexName, constraintName)));
9194 RenameRelationInternal(index_oid, constraintName, false, true);
9197 /* Extra checks needed if making primary key */
9198 if (stmt->primary)
9199 index_check_primary_key(rel, indexInfo, true, stmt);
9201 /* Note we currently don't support EXCLUSION constraints here */
9202 if (stmt->primary)
9203 constraintType = CONSTRAINT_PRIMARY;
9204 else
9205 constraintType = CONSTRAINT_UNIQUE;
9207 /* Create the catalog entries for the constraint */
9208 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9209 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9210 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9211 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9212 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9214 address = index_constraint_create(rel,
9215 index_oid,
9216 InvalidOid,
9217 indexInfo,
9218 constraintName,
9219 constraintType,
9220 flags,
9221 allowSystemTableMods,
9222 false); /* is_internal */
9224 index_close(indexRel, NoLock);
9226 return address;
9230 * ALTER TABLE ADD CONSTRAINT
9232 * Return value is the address of the new constraint; if no constraint was
9233 * added, InvalidObjectAddress is returned.
9235 static ObjectAddress
9236 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9237 Constraint *newConstraint, bool recurse, bool is_readd,
9238 LOCKMODE lockmode)
9240 ObjectAddress address = InvalidObjectAddress;
9242 Assert(IsA(newConstraint, Constraint));
9245 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9246 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9247 * parse_utilcmd.c).
9249 switch (newConstraint->contype)
9251 case CONSTR_CHECK:
9252 case CONSTR_NOTNULL:
9253 address =
9254 ATAddCheckNNConstraint(wqueue, tab, rel,
9255 newConstraint, recurse, false, is_readd,
9256 lockmode);
9257 break;
9259 case CONSTR_FOREIGN:
9262 * Assign or validate constraint name
9264 if (newConstraint->conname)
9266 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9267 RelationGetRelid(rel),
9268 newConstraint->conname))
9269 ereport(ERROR,
9270 (errcode(ERRCODE_DUPLICATE_OBJECT),
9271 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9272 newConstraint->conname,
9273 RelationGetRelationName(rel))));
9275 else
9276 newConstraint->conname =
9277 ChooseConstraintName(RelationGetRelationName(rel),
9278 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9279 "fkey",
9280 RelationGetNamespace(rel),
9281 NIL);
9283 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9284 newConstraint,
9285 recurse, false,
9286 lockmode);
9287 break;
9289 default:
9290 elog(ERROR, "unrecognized constraint type: %d",
9291 (int) newConstraint->contype);
9294 return address;
9298 * Generate the column-name portion of the constraint name for a new foreign
9299 * key given the list of column names that reference the referenced
9300 * table. This will be passed to ChooseConstraintName along with the parent
9301 * table name and the "fkey" suffix.
9303 * We know that less than NAMEDATALEN characters will actually be used, so we
9304 * can truncate the result once we've generated that many.
9306 * XXX see also ChooseExtendedStatisticNameAddition and
9307 * ChooseIndexNameAddition.
9309 static char *
9310 ChooseForeignKeyConstraintNameAddition(List *colnames)
9312 char buf[NAMEDATALEN * 2];
9313 int buflen = 0;
9314 ListCell *lc;
9316 buf[0] = '\0';
9317 foreach(lc, colnames)
9319 const char *name = strVal(lfirst(lc));
9321 if (buflen > 0)
9322 buf[buflen++] = '_'; /* insert _ between names */
9325 * At this point we have buflen <= NAMEDATALEN. name should be less
9326 * than NAMEDATALEN already, but use strlcpy for paranoia.
9328 strlcpy(buf + buflen, name, NAMEDATALEN);
9329 buflen += strlen(buf + buflen);
9330 if (buflen >= NAMEDATALEN)
9331 break;
9333 return pstrdup(buf);
9337 * Add a check or not-null constraint to a single table and its children.
9338 * Returns the address of the constraint added to the parent relation,
9339 * if one gets added, or InvalidObjectAddress otherwise.
9341 * Subroutine for ATExecAddConstraint.
9343 * We must recurse to child tables during execution, rather than using
9344 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9345 * constraints *must* be given the same name, else they won't be seen as
9346 * related later. If the user didn't explicitly specify a name, then
9347 * AddRelationNewConstraints would normally assign different names to the
9348 * child constraints. To fix that, we must capture the name assigned at
9349 * the parent table and pass that down.
9351 static ObjectAddress
9352 ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9353 Constraint *constr, bool recurse, bool recursing,
9354 bool is_readd, LOCKMODE lockmode)
9356 List *newcons;
9357 ListCell *lcon;
9358 List *children;
9359 ListCell *child;
9360 ObjectAddress address = InvalidObjectAddress;
9362 /* At top level, permission check was done in ATPrepCmd, else do it */
9363 if (recursing)
9364 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9367 * Call AddRelationNewConstraints to do the work, making sure it works on
9368 * a copy of the Constraint so transformExpr can't modify the original. It
9369 * returns a list of cooked constraints.
9371 * If the constraint ends up getting merged with a pre-existing one, it's
9372 * omitted from the returned list, which is what we want: we do not need
9373 * to do any validation work. That can only happen at child tables,
9374 * though, since we disallow merging at the top level.
9376 newcons = AddRelationNewConstraints(rel, NIL,
9377 list_make1(copyObject(constr)),
9378 recursing || is_readd, /* allow_merge */
9379 !recursing, /* is_local */
9380 is_readd, /* is_internal */
9381 NULL); /* queryString not available
9382 * here */
9384 /* we don't expect more than one constraint here */
9385 Assert(list_length(newcons) <= 1);
9387 /* Add each to-be-validated constraint to Phase 3's queue */
9388 foreach(lcon, newcons)
9390 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9392 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9394 NewConstraint *newcon;
9396 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9397 newcon->name = ccon->name;
9398 newcon->contype = ccon->contype;
9399 newcon->qual = ccon->expr;
9401 tab->constraints = lappend(tab->constraints, newcon);
9404 /* Save the actually assigned name if it was defaulted */
9405 if (constr->conname == NULL)
9406 constr->conname = ccon->name;
9409 * If adding a not-null constraint, set the pg_attribute flag and tell
9410 * phase 3 to verify existing rows, if needed.
9412 if (constr->contype == CONSTR_NOTNULL)
9413 set_attnotnull(wqueue, rel, ccon->attnum,
9414 !ccon->is_no_inherit, lockmode);
9416 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9419 /* At this point we must have a locked-down name to use */
9420 Assert(newcons == NIL || constr->conname != NULL);
9422 /* Advance command counter in case same table is visited multiple times */
9423 CommandCounterIncrement();
9426 * If the constraint got merged with an existing constraint, we're done.
9427 * We mustn't recurse to child tables in this case, because they've
9428 * already got the constraint, and visiting them again would lead to an
9429 * incorrect value for coninhcount.
9431 if (newcons == NIL)
9432 return address;
9435 * If adding a NO INHERIT constraint, no need to find our children.
9437 if (constr->is_no_inherit)
9438 return address;
9441 * Propagate to children as appropriate. Unlike most other ALTER
9442 * routines, we have to do this one level of recursion at a time; we can't
9443 * use find_all_inheritors to do it in one pass.
9445 children =
9446 find_inheritance_children(RelationGetRelid(rel), lockmode);
9449 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9450 * constraint creation only if there are no children currently. Error out
9451 * otherwise.
9453 if (!recurse && children != NIL)
9454 ereport(ERROR,
9455 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9456 errmsg("constraint must be added to child tables too")));
9459 * The constraint must appear as inherited in children, so create a
9460 * modified constraint object to use.
9462 constr = copyObject(constr);
9463 constr->inhcount = 1;
9464 foreach(child, children)
9466 Oid childrelid = lfirst_oid(child);
9467 Relation childrel;
9468 AlteredTableInfo *childtab;
9470 /* find_inheritance_children already got lock */
9471 childrel = table_open(childrelid, NoLock);
9472 CheckTableNotInUse(childrel, "ALTER TABLE");
9474 /* Find or create work queue entry for this table */
9475 childtab = ATGetQueueEntry(wqueue, childrel);
9478 * Recurse to child. XXX if we didn't create a constraint on the
9479 * parent because it already existed, and we do create one on a child,
9480 * should we return that child's constraint ObjectAddress here?
9482 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9483 constr, recurse, true, is_readd, lockmode);
9485 table_close(childrel, NoLock);
9488 return address;
9492 * Add a foreign-key constraint to a single table; return the new constraint's
9493 * address.
9495 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9496 * lock on the rel, and have done appropriate validity checks for it.
9497 * We do permissions checks here, however.
9499 * When the referenced or referencing tables (or both) are partitioned,
9500 * multiple pg_constraint rows are required -- one for each partitioned table
9501 * and each partition on each side (fortunately, not one for every combination
9502 * thereof). We also need action triggers on each leaf partition on the
9503 * referenced side, and check triggers on each leaf partition on the
9504 * referencing side.
9506 static ObjectAddress
9507 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9508 Constraint *fkconstraint,
9509 bool recurse, bool recursing, LOCKMODE lockmode)
9511 Relation pkrel;
9512 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9513 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9514 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9515 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9516 Oid opclasses[INDEX_MAX_KEYS] = {0};
9517 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9518 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9519 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9520 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9521 int i;
9522 int numfks,
9523 numpks,
9524 numfkdelsetcols;
9525 Oid indexOid;
9526 bool old_check_ok;
9527 ObjectAddress address;
9528 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9531 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9532 * delete rows out from under us.
9534 if (OidIsValid(fkconstraint->old_pktable_oid))
9535 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9536 else
9537 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9540 * Validity checks (permission checks wait till we have the column
9541 * numbers)
9543 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9545 if (!recurse)
9546 ereport(ERROR,
9547 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9548 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9549 RelationGetRelationName(rel),
9550 RelationGetRelationName(pkrel))));
9551 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9552 ereport(ERROR,
9553 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9554 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9555 RelationGetRelationName(rel),
9556 RelationGetRelationName(pkrel)),
9557 errdetail("This feature is not yet supported on partitioned tables.")));
9560 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9561 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9562 ereport(ERROR,
9563 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9564 errmsg("referenced relation \"%s\" is not a table",
9565 RelationGetRelationName(pkrel))));
9567 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9568 ereport(ERROR,
9569 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9570 errmsg("permission denied: \"%s\" is a system catalog",
9571 RelationGetRelationName(pkrel))));
9574 * References from permanent or unlogged tables to temp tables, and from
9575 * permanent tables to unlogged tables, are disallowed because the
9576 * referenced data can vanish out from under us. References from temp
9577 * tables to any other table type are also disallowed, because other
9578 * backends might need to run the RI triggers on the perm table, but they
9579 * can't reliably see tuples in the local buffers of other backends.
9581 switch (rel->rd_rel->relpersistence)
9583 case RELPERSISTENCE_PERMANENT:
9584 if (!RelationIsPermanent(pkrel))
9585 ereport(ERROR,
9586 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9587 errmsg("constraints on permanent tables may reference only permanent tables")));
9588 break;
9589 case RELPERSISTENCE_UNLOGGED:
9590 if (!RelationIsPermanent(pkrel)
9591 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9592 ereport(ERROR,
9593 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9594 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9595 break;
9596 case RELPERSISTENCE_TEMP:
9597 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9598 ereport(ERROR,
9599 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9600 errmsg("constraints on temporary tables may reference only temporary tables")));
9601 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9602 ereport(ERROR,
9603 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9604 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9605 break;
9609 * Look up the referencing attributes to make sure they exist, and record
9610 * their attnums and type OIDs.
9612 numfks = transformColumnNameList(RelationGetRelid(rel),
9613 fkconstraint->fk_attrs,
9614 fkattnum, fktypoid);
9616 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9617 fkconstraint->fk_del_set_cols,
9618 fkdelsetcols, NULL);
9619 validateFkOnDeleteSetColumns(numfks, fkattnum,
9620 numfkdelsetcols, fkdelsetcols,
9621 fkconstraint->fk_del_set_cols);
9624 * If the attribute list for the referenced table was omitted, lookup the
9625 * definition of the primary key and use it. Otherwise, validate the
9626 * supplied attribute list. In either case, discover the index OID and
9627 * index opclasses, and the attnums and type OIDs of the attributes.
9629 if (fkconstraint->pk_attrs == NIL)
9631 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9632 &fkconstraint->pk_attrs,
9633 pkattnum, pktypoid,
9634 opclasses);
9636 else
9638 numpks = transformColumnNameList(RelationGetRelid(pkrel),
9639 fkconstraint->pk_attrs,
9640 pkattnum, pktypoid);
9641 /* Look for an index matching the column list */
9642 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9643 opclasses);
9647 * Now we can check permissions.
9649 checkFkeyPermissions(pkrel, pkattnum, numpks);
9652 * Check some things for generated columns.
9654 for (i = 0; i < numfks; i++)
9656 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9658 if (attgenerated)
9661 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9663 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9664 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9665 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9666 ereport(ERROR,
9667 (errcode(ERRCODE_SYNTAX_ERROR),
9668 errmsg("invalid %s action for foreign key constraint containing generated column",
9669 "ON UPDATE")));
9670 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9671 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9672 ereport(ERROR,
9673 (errcode(ERRCODE_SYNTAX_ERROR),
9674 errmsg("invalid %s action for foreign key constraint containing generated column",
9675 "ON DELETE")));
9680 * Look up the equality operators to use in the constraint.
9682 * Note that we have to be careful about the difference between the actual
9683 * PK column type and the opclass' declared input type, which might be
9684 * only binary-compatible with it. The declared opcintype is the right
9685 * thing to probe pg_amop with.
9687 if (numfks != numpks)
9688 ereport(ERROR,
9689 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9690 errmsg("number of referencing and referenced columns for foreign key disagree")));
9693 * On the strength of a previous constraint, we might avoid scanning
9694 * tables to validate this one. See below.
9696 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9697 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9699 for (i = 0; i < numpks; i++)
9701 Oid pktype = pktypoid[i];
9702 Oid fktype = fktypoid[i];
9703 Oid fktyped;
9704 HeapTuple cla_ht;
9705 Form_pg_opclass cla_tup;
9706 Oid amid;
9707 Oid opfamily;
9708 Oid opcintype;
9709 Oid pfeqop;
9710 Oid ppeqop;
9711 Oid ffeqop;
9712 int16 eqstrategy;
9713 Oid pfeqop_right;
9715 /* We need several fields out of the pg_opclass entry */
9716 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9717 if (!HeapTupleIsValid(cla_ht))
9718 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9719 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9720 amid = cla_tup->opcmethod;
9721 opfamily = cla_tup->opcfamily;
9722 opcintype = cla_tup->opcintype;
9723 ReleaseSysCache(cla_ht);
9726 * Check it's a btree; currently this can never fail since no other
9727 * index AMs support unique indexes. If we ever did have other types
9728 * of unique indexes, we'd need a way to determine which operator
9729 * strategy number is equality. (Is it reasonable to insist that
9730 * every such index AM use btree's number for equality?)
9732 if (amid != BTREE_AM_OID)
9733 elog(ERROR, "only b-tree indexes are supported for foreign keys");
9734 eqstrategy = BTEqualStrategyNumber;
9737 * There had better be a primary equality operator for the index.
9738 * We'll use it for PK = PK comparisons.
9740 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9741 eqstrategy);
9743 if (!OidIsValid(ppeqop))
9744 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9745 eqstrategy, opcintype, opcintype, opfamily);
9748 * Are there equality operators that take exactly the FK type? Assume
9749 * we should look through any domain here.
9751 fktyped = getBaseType(fktype);
9753 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9754 eqstrategy);
9755 if (OidIsValid(pfeqop))
9757 pfeqop_right = fktyped;
9758 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9759 eqstrategy);
9761 else
9763 /* keep compiler quiet */
9764 pfeqop_right = InvalidOid;
9765 ffeqop = InvalidOid;
9768 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9771 * Otherwise, look for an implicit cast from the FK type to the
9772 * opcintype, and if found, use the primary equality operator.
9773 * This is a bit tricky because opcintype might be a polymorphic
9774 * type such as ANYARRAY or ANYENUM; so what we have to test is
9775 * whether the two actual column types can be concurrently cast to
9776 * that type. (Otherwise, we'd fail to reject combinations such
9777 * as int[] and point[].)
9779 Oid input_typeids[2];
9780 Oid target_typeids[2];
9782 input_typeids[0] = pktype;
9783 input_typeids[1] = fktype;
9784 target_typeids[0] = opcintype;
9785 target_typeids[1] = opcintype;
9786 if (can_coerce_type(2, input_typeids, target_typeids,
9787 COERCION_IMPLICIT))
9789 pfeqop = ffeqop = ppeqop;
9790 pfeqop_right = opcintype;
9794 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9795 ereport(ERROR,
9796 (errcode(ERRCODE_DATATYPE_MISMATCH),
9797 errmsg("foreign key constraint \"%s\" cannot be implemented",
9798 fkconstraint->conname),
9799 errdetail("Key columns \"%s\" and \"%s\" "
9800 "are of incompatible types: %s and %s.",
9801 strVal(list_nth(fkconstraint->fk_attrs, i)),
9802 strVal(list_nth(fkconstraint->pk_attrs, i)),
9803 format_type_be(fktype),
9804 format_type_be(pktype))));
9806 if (old_check_ok)
9809 * When a pfeqop changes, revalidate the constraint. We could
9810 * permit intra-opfamily changes, but that adds subtle complexity
9811 * without any concrete benefit for core types. We need not
9812 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9814 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9815 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9816 old_pfeqop_item);
9818 if (old_check_ok)
9820 Oid old_fktype;
9821 Oid new_fktype;
9822 CoercionPathType old_pathtype;
9823 CoercionPathType new_pathtype;
9824 Oid old_castfunc;
9825 Oid new_castfunc;
9826 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9827 fkattnum[i] - 1);
9830 * Identify coercion pathways from each of the old and new FK-side
9831 * column types to the right (foreign) operand type of the pfeqop.
9832 * We may assume that pg_constraint.conkey is not changing.
9834 old_fktype = attr->atttypid;
9835 new_fktype = fktype;
9836 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9837 &old_castfunc);
9838 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9839 &new_castfunc);
9842 * Upon a change to the cast from the FK column to its pfeqop
9843 * operand, revalidate the constraint. For this evaluation, a
9844 * binary coercion cast is equivalent to no cast at all. While
9845 * type implementors should design implicit casts with an eye
9846 * toward consistency of operations like equality, we cannot
9847 * assume here that they have done so.
9849 * A function with a polymorphic argument could change behavior
9850 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9851 * when the cast destination is polymorphic, we only avoid
9852 * revalidation if the input type has not changed at all. Given
9853 * just the core data types and operator classes, this requirement
9854 * prevents no would-be optimizations.
9856 * If the cast converts from a base type to a domain thereon, then
9857 * that domain type must be the opcintype of the unique index.
9858 * Necessarily, the primary key column must then be of the domain
9859 * type. Since the constraint was previously valid, all values on
9860 * the foreign side necessarily exist on the primary side and in
9861 * turn conform to the domain. Consequently, we need not treat
9862 * domains specially here.
9864 * Since we require that all collations share the same notion of
9865 * equality (which they do, because texteq reduces to bitwise
9866 * equality), we don't compare collation here.
9868 * We need not directly consider the PK type. It's necessarily
9869 * binary coercible to the opcintype of the unique index column,
9870 * and ri_triggers.c will only deal with PK datums in terms of
9871 * that opcintype. Changing the opcintype also changes pfeqop.
9873 old_check_ok = (new_pathtype == old_pathtype &&
9874 new_castfunc == old_castfunc &&
9875 (!IsPolymorphicType(pfeqop_right) ||
9876 new_fktype == old_fktype));
9879 pfeqoperators[i] = pfeqop;
9880 ppeqoperators[i] = ppeqop;
9881 ffeqoperators[i] = ffeqop;
9885 * Create all the constraint and trigger objects, recursing to partitions
9886 * as necessary. First handle the referenced side.
9888 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9889 indexOid,
9890 InvalidOid, /* no parent constraint */
9891 numfks,
9892 pkattnum,
9893 fkattnum,
9894 pfeqoperators,
9895 ppeqoperators,
9896 ffeqoperators,
9897 numfkdelsetcols,
9898 fkdelsetcols,
9899 old_check_ok,
9900 InvalidOid, InvalidOid);
9902 /* Now handle the referencing side. */
9903 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9904 indexOid,
9905 address.objectId,
9906 numfks,
9907 pkattnum,
9908 fkattnum,
9909 pfeqoperators,
9910 ppeqoperators,
9911 ffeqoperators,
9912 numfkdelsetcols,
9913 fkdelsetcols,
9914 old_check_ok,
9915 lockmode,
9916 InvalidOid, InvalidOid);
9919 * Done. Close pk table, but keep lock until we've committed.
9921 table_close(pkrel, NoLock);
9923 return address;
9927 * validateFkOnDeleteSetColumns
9928 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9929 * column lists are valid.
9931 void
9932 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
9933 int numfksetcols, const int16 *fksetcolsattnums,
9934 List *fksetcols)
9936 for (int i = 0; i < numfksetcols; i++)
9938 int16 setcol_attnum = fksetcolsattnums[i];
9939 bool seen = false;
9941 for (int j = 0; j < numfks; j++)
9943 if (fkattnums[j] == setcol_attnum)
9945 seen = true;
9946 break;
9950 if (!seen)
9952 char *col = strVal(list_nth(fksetcols, i));
9954 ereport(ERROR,
9955 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
9956 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
9962 * addFkRecurseReferenced
9963 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9964 * side of the constraint
9966 * Create pg_constraint rows for the referenced side of the constraint,
9967 * referencing the parent of the referencing side; also create action triggers
9968 * on leaf partitions. If the table is partitioned, recurse to handle each
9969 * partition.
9971 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9972 * of an ALTER TABLE sequence.
9973 * fkconstraint is the constraint being added.
9974 * rel is the root referencing relation.
9975 * pkrel is the referenced relation; might be a partition, if recursing.
9976 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9977 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
9978 * top-level constraint.
9979 * numfks is the number of columns in the foreign key
9980 * pkattnum is the attnum array of referenced attributes.
9981 * fkattnum is the attnum array of referencing attributes.
9982 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9983 * (...) clause
9984 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9985 * NULL/DEFAULT clause
9986 * pf/pp/ffeqoperators are OID array of operators between columns.
9987 * old_check_ok signals that this constraint replaces an existing one that
9988 * was already validated (thus this one doesn't need validation).
9989 * parentDelTrigger and parentUpdTrigger, when being recursively called on
9990 * a partition, are the OIDs of the parent action triggers for DELETE and
9991 * UPDATE respectively.
9993 static ObjectAddress
9994 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
9995 Relation pkrel, Oid indexOid, Oid parentConstr,
9996 int numfks,
9997 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
9998 Oid *ppeqoperators, Oid *ffeqoperators,
9999 int numfkdelsetcols, int16 *fkdelsetcols,
10000 bool old_check_ok,
10001 Oid parentDelTrigger, Oid parentUpdTrigger)
10003 ObjectAddress address;
10004 Oid constrOid;
10005 char *conname;
10006 bool conislocal;
10007 int coninhcount;
10008 bool connoinherit;
10009 Oid deleteTriggerOid,
10010 updateTriggerOid;
10013 * Verify relkind for each referenced partition. At the top level, this
10014 * is redundant with a previous check, but we need it when recursing.
10016 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10017 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10018 ereport(ERROR,
10019 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10020 errmsg("referenced relation \"%s\" is not a table",
10021 RelationGetRelationName(pkrel))));
10024 * Caller supplies us with a constraint name; however, it may be used in
10025 * this partition, so come up with a different one in that case.
10027 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10028 RelationGetRelid(rel),
10029 fkconstraint->conname))
10030 conname = ChooseConstraintName(RelationGetRelationName(rel),
10031 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10032 "fkey",
10033 RelationGetNamespace(rel), NIL);
10034 else
10035 conname = fkconstraint->conname;
10037 if (OidIsValid(parentConstr))
10039 conislocal = false;
10040 coninhcount = 1;
10041 connoinherit = false;
10043 else
10045 conislocal = true;
10046 coninhcount = 0;
10049 * always inherit for partitioned tables, never for legacy inheritance
10051 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10055 * Record the FK constraint in pg_constraint.
10057 constrOid = CreateConstraintEntry(conname,
10058 RelationGetNamespace(rel),
10059 CONSTRAINT_FOREIGN,
10060 fkconstraint->deferrable,
10061 fkconstraint->initdeferred,
10062 fkconstraint->initially_valid,
10063 parentConstr,
10064 RelationGetRelid(rel),
10065 fkattnum,
10066 numfks,
10067 numfks,
10068 InvalidOid, /* not a domain constraint */
10069 indexOid,
10070 RelationGetRelid(pkrel),
10071 pkattnum,
10072 pfeqoperators,
10073 ppeqoperators,
10074 ffeqoperators,
10075 numfks,
10076 fkconstraint->fk_upd_action,
10077 fkconstraint->fk_del_action,
10078 fkdelsetcols,
10079 numfkdelsetcols,
10080 fkconstraint->fk_matchtype,
10081 NULL, /* no exclusion constraint */
10082 NULL, /* no check constraint */
10083 NULL,
10084 conislocal, /* islocal */
10085 coninhcount, /* inhcount */
10086 connoinherit, /* conNoInherit */
10087 false); /* is_internal */
10089 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10092 * Mark the child constraint as part of the parent constraint; it must not
10093 * be dropped on its own. (This constraint is deleted when the partition
10094 * is detached, but a special check needs to occur that the partition
10095 * contains no referenced values.)
10097 if (OidIsValid(parentConstr))
10099 ObjectAddress referenced;
10101 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10102 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10105 /* make new constraint visible, in case we add more */
10106 CommandCounterIncrement();
10109 * Create the action triggers that enforce the constraint.
10111 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10112 fkconstraint,
10113 constrOid, indexOid,
10114 parentDelTrigger, parentUpdTrigger,
10115 &deleteTriggerOid, &updateTriggerOid);
10118 * If the referenced table is partitioned, recurse on ourselves to handle
10119 * each partition. We need one pg_constraint row created for each
10120 * partition in addition to the pg_constraint row for the parent table.
10122 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10124 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10126 for (int i = 0; i < pd->nparts; i++)
10128 Relation partRel;
10129 AttrMap *map;
10130 AttrNumber *mapped_pkattnum;
10131 Oid partIndexId;
10133 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10136 * Map the attribute numbers in the referenced side of the FK
10137 * definition to match the partition's column layout.
10139 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10140 RelationGetDescr(pkrel),
10141 false);
10142 if (map)
10144 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10145 for (int j = 0; j < numfks; j++)
10146 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10148 else
10149 mapped_pkattnum = pkattnum;
10151 /* do the deed */
10152 partIndexId = index_get_partition(partRel, indexOid);
10153 if (!OidIsValid(partIndexId))
10154 elog(ERROR, "index for %u not found in partition %s",
10155 indexOid, RelationGetRelationName(partRel));
10156 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10157 partIndexId, constrOid, numfks,
10158 mapped_pkattnum, fkattnum,
10159 pfeqoperators, ppeqoperators, ffeqoperators,
10160 numfkdelsetcols, fkdelsetcols,
10161 old_check_ok,
10162 deleteTriggerOid, updateTriggerOid);
10164 /* Done -- clean up (but keep the lock) */
10165 table_close(partRel, NoLock);
10166 if (map)
10168 pfree(mapped_pkattnum);
10169 free_attrmap(map);
10174 return address;
10178 * addFkRecurseReferencing
10179 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10181 * If the referencing relation is a plain relation, create the necessary check
10182 * triggers that implement the constraint, and set up for Phase 3 constraint
10183 * verification. If the referencing relation is a partitioned table, then
10184 * we create a pg_constraint row for it and recurse on this routine for each
10185 * partition.
10187 * We assume that the referenced relation is locked against concurrent
10188 * deletions. If it's a partitioned relation, every partition must be so
10189 * locked.
10191 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10192 * of an ALTER TABLE sequence.
10193 * fkconstraint is the constraint being added.
10194 * rel is the referencing relation; might be a partition, if recursing.
10195 * pkrel is the root referenced relation.
10196 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10197 * parentConstr is the OID of the parent constraint (there is always one).
10198 * numfks is the number of columns in the foreign key
10199 * pkattnum is the attnum array of referenced attributes.
10200 * fkattnum is the attnum array of referencing attributes.
10201 * pf/pp/ffeqoperators are OID array of operators between columns.
10202 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10203 * (...) clause
10204 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10205 * NULL/DEFAULT clause
10206 * old_check_ok signals that this constraint replaces an existing one that
10207 * was already validated (thus this one doesn't need validation).
10208 * lockmode is the lockmode to acquire on partitions when recursing.
10209 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10210 * a partition, are the OIDs of the parent check triggers for INSERT and
10211 * UPDATE respectively.
10213 static void
10214 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10215 Relation pkrel, Oid indexOid, Oid parentConstr,
10216 int numfks, int16 *pkattnum, int16 *fkattnum,
10217 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10218 int numfkdelsetcols, int16 *fkdelsetcols,
10219 bool old_check_ok, LOCKMODE lockmode,
10220 Oid parentInsTrigger, Oid parentUpdTrigger)
10222 Oid insertTriggerOid,
10223 updateTriggerOid;
10225 Assert(OidIsValid(parentConstr));
10227 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10228 ereport(ERROR,
10229 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10230 errmsg("foreign key constraints are not supported on foreign tables")));
10233 * Add the check triggers to it and, if necessary, schedule it to be
10234 * checked in Phase 3.
10236 * If the relation is partitioned, drill down to do it to its partitions.
10238 createForeignKeyCheckTriggers(RelationGetRelid(rel),
10239 RelationGetRelid(pkrel),
10240 fkconstraint,
10241 parentConstr,
10242 indexOid,
10243 parentInsTrigger, parentUpdTrigger,
10244 &insertTriggerOid, &updateTriggerOid);
10246 if (rel->rd_rel->relkind == RELKIND_RELATION)
10249 * Tell Phase 3 to check that the constraint is satisfied by existing
10250 * rows. We can skip this during table creation, when requested
10251 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10252 * and when we're recreating a constraint following a SET DATA TYPE
10253 * operation that did not impugn its validity.
10255 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10257 NewConstraint *newcon;
10258 AlteredTableInfo *tab;
10260 tab = ATGetQueueEntry(wqueue, rel);
10262 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10263 newcon->name = get_constraint_name(parentConstr);
10264 newcon->contype = CONSTR_FOREIGN;
10265 newcon->refrelid = RelationGetRelid(pkrel);
10266 newcon->refindid = indexOid;
10267 newcon->conid = parentConstr;
10268 newcon->qual = (Node *) fkconstraint;
10270 tab->constraints = lappend(tab->constraints, newcon);
10273 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10275 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10276 Relation trigrel;
10279 * Triggers of the foreign keys will be manipulated a bunch of times
10280 * in the loop below. To avoid repeatedly opening/closing the trigger
10281 * catalog relation, we open it here and pass it to the subroutines
10282 * called below.
10284 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10287 * Recurse to take appropriate action on each partition; either we
10288 * find an existing constraint to reparent to ours, or we create a new
10289 * one.
10291 for (int i = 0; i < pd->nparts; i++)
10293 Oid partitionId = pd->oids[i];
10294 Relation partition = table_open(partitionId, lockmode);
10295 List *partFKs;
10296 AttrMap *attmap;
10297 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10298 bool attached;
10299 char *conname;
10300 Oid constrOid;
10301 ObjectAddress address,
10302 referenced;
10303 ListCell *cell;
10305 CheckTableNotInUse(partition, "ALTER TABLE");
10307 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10308 RelationGetDescr(rel),
10309 false);
10310 for (int j = 0; j < numfks; j++)
10311 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10313 /* Check whether an existing constraint can be repurposed */
10314 partFKs = copyObject(RelationGetFKeyList(partition));
10315 attached = false;
10316 foreach(cell, partFKs)
10318 ForeignKeyCacheInfo *fk;
10320 fk = lfirst_node(ForeignKeyCacheInfo, cell);
10321 if (tryAttachPartitionForeignKey(fk,
10322 partitionId,
10323 parentConstr,
10324 numfks,
10325 mapped_fkattnum,
10326 pkattnum,
10327 pfeqoperators,
10328 insertTriggerOid,
10329 updateTriggerOid,
10330 trigrel))
10332 attached = true;
10333 break;
10336 if (attached)
10338 table_close(partition, NoLock);
10339 continue;
10343 * No luck finding a good constraint to reuse; create our own.
10345 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10346 RelationGetRelid(partition),
10347 fkconstraint->conname))
10348 conname = ChooseConstraintName(RelationGetRelationName(partition),
10349 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10350 "fkey",
10351 RelationGetNamespace(partition), NIL);
10352 else
10353 conname = fkconstraint->conname;
10354 constrOid =
10355 CreateConstraintEntry(conname,
10356 RelationGetNamespace(partition),
10357 CONSTRAINT_FOREIGN,
10358 fkconstraint->deferrable,
10359 fkconstraint->initdeferred,
10360 fkconstraint->initially_valid,
10361 parentConstr,
10362 partitionId,
10363 mapped_fkattnum,
10364 numfks,
10365 numfks,
10366 InvalidOid,
10367 indexOid,
10368 RelationGetRelid(pkrel),
10369 pkattnum,
10370 pfeqoperators,
10371 ppeqoperators,
10372 ffeqoperators,
10373 numfks,
10374 fkconstraint->fk_upd_action,
10375 fkconstraint->fk_del_action,
10376 fkdelsetcols,
10377 numfkdelsetcols,
10378 fkconstraint->fk_matchtype,
10379 NULL,
10380 NULL,
10381 NULL,
10382 false,
10384 false,
10385 false);
10388 * Give this constraint partition-type dependencies on the parent
10389 * constraint as well as the table.
10391 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10392 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10393 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10394 ObjectAddressSet(referenced, RelationRelationId, partitionId);
10395 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10397 /* Make all this visible before recursing */
10398 CommandCounterIncrement();
10400 /* call ourselves to finalize the creation and we're done */
10401 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10402 indexOid,
10403 constrOid,
10404 numfks,
10405 pkattnum,
10406 mapped_fkattnum,
10407 pfeqoperators,
10408 ppeqoperators,
10409 ffeqoperators,
10410 numfkdelsetcols,
10411 fkdelsetcols,
10412 old_check_ok,
10413 lockmode,
10414 insertTriggerOid,
10415 updateTriggerOid);
10417 table_close(partition, NoLock);
10420 table_close(trigrel, RowExclusiveLock);
10425 * CloneForeignKeyConstraints
10426 * Clone foreign keys from a partitioned table to a newly acquired
10427 * partition.
10429 * partitionRel is a partition of parentRel, so we can be certain that it has
10430 * the same columns with the same datatypes. The columns may be in different
10431 * order, though.
10433 * wqueue must be passed to set up phase 3 constraint checking, unless the
10434 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10435 * PARTITION OF).
10437 static void
10438 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10439 Relation partitionRel)
10441 /* This only works for declarative partitioning */
10442 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10445 * Clone constraints for which the parent is on the referenced side.
10447 CloneFkReferenced(parentRel, partitionRel);
10450 * Now clone constraints where the parent is on the referencing side.
10452 CloneFkReferencing(wqueue, parentRel, partitionRel);
10456 * CloneFkReferenced
10457 * Subroutine for CloneForeignKeyConstraints
10459 * Find all the FKs that have the parent relation on the referenced side;
10460 * clone those constraints to the given partition. This is to be called
10461 * when the partition is being created or attached.
10463 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10465 * This recurses to partitions, if the relation being attached is partitioned.
10466 * Recursion is done by calling addFkRecurseReferenced.
10468 static void
10469 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10471 Relation pg_constraint;
10472 AttrMap *attmap;
10473 ListCell *cell;
10474 SysScanDesc scan;
10475 ScanKeyData key[2];
10476 HeapTuple tuple;
10477 List *clone = NIL;
10478 Relation trigrel;
10481 * Search for any constraints where this partition's parent is in the
10482 * referenced side. However, we must not clone any constraint whose
10483 * parent constraint is also going to be cloned, to avoid duplicates. So
10484 * do it in two steps: first construct the list of constraints to clone,
10485 * then go over that list cloning those whose parents are not in the list.
10486 * (We must not rely on the parent being seen first, since the catalog
10487 * scan could return children first.)
10489 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10490 ScanKeyInit(&key[0],
10491 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10492 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10493 ScanKeyInit(&key[1],
10494 Anum_pg_constraint_contype, BTEqualStrategyNumber,
10495 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10496 /* This is a seqscan, as we don't have a usable index ... */
10497 scan = systable_beginscan(pg_constraint, InvalidOid, true,
10498 NULL, 2, key);
10499 while ((tuple = systable_getnext(scan)) != NULL)
10501 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10503 clone = lappend_oid(clone, constrForm->oid);
10505 systable_endscan(scan);
10506 table_close(pg_constraint, RowShareLock);
10509 * Triggers of the foreign keys will be manipulated a bunch of times in
10510 * the loop below. To avoid repeatedly opening/closing the trigger
10511 * catalog relation, we open it here and pass it to the subroutines called
10512 * below.
10514 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10516 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10517 RelationGetDescr(parentRel),
10518 false);
10519 foreach(cell, clone)
10521 Oid constrOid = lfirst_oid(cell);
10522 Form_pg_constraint constrForm;
10523 Relation fkRel;
10524 Oid indexOid;
10525 Oid partIndexId;
10526 int numfks;
10527 AttrNumber conkey[INDEX_MAX_KEYS];
10528 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10529 AttrNumber confkey[INDEX_MAX_KEYS];
10530 Oid conpfeqop[INDEX_MAX_KEYS];
10531 Oid conppeqop[INDEX_MAX_KEYS];
10532 Oid conffeqop[INDEX_MAX_KEYS];
10533 int numfkdelsetcols;
10534 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10535 Constraint *fkconstraint;
10536 Oid deleteTriggerOid,
10537 updateTriggerOid;
10539 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10540 if (!HeapTupleIsValid(tuple))
10541 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10542 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10545 * As explained above: don't try to clone a constraint for which we're
10546 * going to clone the parent.
10548 if (list_member_oid(clone, constrForm->conparentid))
10550 ReleaseSysCache(tuple);
10551 continue;
10555 * Don't clone self-referencing foreign keys, which can be in the
10556 * partitioned table or in the partition-to-be.
10558 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10559 constrForm->conrelid == RelationGetRelid(partitionRel))
10561 ReleaseSysCache(tuple);
10562 continue;
10566 * Because we're only expanding the key space at the referenced side,
10567 * we don't need to prevent any operation in the referencing table, so
10568 * AccessShareLock suffices (assumes that dropping the constraint
10569 * acquires AEL).
10571 fkRel = table_open(constrForm->conrelid, AccessShareLock);
10573 indexOid = constrForm->conindid;
10574 DeconstructFkConstraintRow(tuple,
10575 &numfks,
10576 conkey,
10577 confkey,
10578 conpfeqop,
10579 conppeqop,
10580 conffeqop,
10581 &numfkdelsetcols,
10582 confdelsetcols);
10584 for (int i = 0; i < numfks; i++)
10585 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10587 fkconstraint = makeNode(Constraint);
10588 fkconstraint->contype = CONSTRAINT_FOREIGN;
10589 fkconstraint->conname = NameStr(constrForm->conname);
10590 fkconstraint->deferrable = constrForm->condeferrable;
10591 fkconstraint->initdeferred = constrForm->condeferred;
10592 fkconstraint->location = -1;
10593 fkconstraint->pktable = NULL;
10594 /* ->fk_attrs determined below */
10595 fkconstraint->pk_attrs = NIL;
10596 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10597 fkconstraint->fk_upd_action = constrForm->confupdtype;
10598 fkconstraint->fk_del_action = constrForm->confdeltype;
10599 fkconstraint->fk_del_set_cols = NIL;
10600 fkconstraint->old_conpfeqop = NIL;
10601 fkconstraint->old_pktable_oid = InvalidOid;
10602 fkconstraint->skip_validation = false;
10603 fkconstraint->initially_valid = true;
10605 /* set up colnames that are used to generate the constraint name */
10606 for (int i = 0; i < numfks; i++)
10608 Form_pg_attribute att;
10610 att = TupleDescAttr(RelationGetDescr(fkRel),
10611 conkey[i] - 1);
10612 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10613 makeString(NameStr(att->attname)));
10617 * Add the new foreign key constraint pointing to the new partition.
10618 * Because this new partition appears in the referenced side of the
10619 * constraint, we don't need to set up for Phase 3 check.
10621 partIndexId = index_get_partition(partitionRel, indexOid);
10622 if (!OidIsValid(partIndexId))
10623 elog(ERROR, "index for %u not found in partition %s",
10624 indexOid, RelationGetRelationName(partitionRel));
10627 * Get the "action" triggers belonging to the constraint to pass as
10628 * parent OIDs for similar triggers that will be created on the
10629 * partition in addFkRecurseReferenced().
10631 GetForeignKeyActionTriggers(trigrel, constrOid,
10632 constrForm->confrelid, constrForm->conrelid,
10633 &deleteTriggerOid, &updateTriggerOid);
10635 addFkRecurseReferenced(NULL,
10636 fkconstraint,
10637 fkRel,
10638 partitionRel,
10639 partIndexId,
10640 constrOid,
10641 numfks,
10642 mapped_confkey,
10643 conkey,
10644 conpfeqop,
10645 conppeqop,
10646 conffeqop,
10647 numfkdelsetcols,
10648 confdelsetcols,
10649 true,
10650 deleteTriggerOid,
10651 updateTriggerOid);
10653 table_close(fkRel, NoLock);
10654 ReleaseSysCache(tuple);
10657 table_close(trigrel, RowExclusiveLock);
10661 * CloneFkReferencing
10662 * Subroutine for CloneForeignKeyConstraints
10664 * For each FK constraint of the parent relation in the given list, find an
10665 * equivalent constraint in its partition relation that can be reparented;
10666 * if one cannot be found, create a new constraint in the partition as its
10667 * child.
10669 * If wqueue is given, it is used to set up phase-3 verification for each
10670 * cloned constraint; if omitted, we assume that such verification is not
10671 * needed (example: the partition is being created anew).
10673 static void
10674 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
10676 AttrMap *attmap;
10677 List *partFKs;
10678 List *clone = NIL;
10679 ListCell *cell;
10680 Relation trigrel;
10682 /* obtain a list of constraints that we need to clone */
10683 foreach(cell, RelationGetFKeyList(parentRel))
10685 ForeignKeyCacheInfo *fk = lfirst(cell);
10687 clone = lappend_oid(clone, fk->conoid);
10691 * Silently do nothing if there's nothing to do. In particular, this
10692 * avoids throwing a spurious error for foreign tables.
10694 if (clone == NIL)
10695 return;
10697 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10698 ereport(ERROR,
10699 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10700 errmsg("foreign key constraints are not supported on foreign tables")));
10703 * Triggers of the foreign keys will be manipulated a bunch of times in
10704 * the loop below. To avoid repeatedly opening/closing the trigger
10705 * catalog relation, we open it here and pass it to the subroutines called
10706 * below.
10708 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10711 * The constraint key may differ, if the columns in the partition are
10712 * different. This map is used to convert them.
10714 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10715 RelationGetDescr(parentRel),
10716 false);
10718 partFKs = copyObject(RelationGetFKeyList(partRel));
10720 foreach(cell, clone)
10722 Oid parentConstrOid = lfirst_oid(cell);
10723 Form_pg_constraint constrForm;
10724 Relation pkrel;
10725 HeapTuple tuple;
10726 int numfks;
10727 AttrNumber conkey[INDEX_MAX_KEYS];
10728 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10729 AttrNumber confkey[INDEX_MAX_KEYS];
10730 Oid conpfeqop[INDEX_MAX_KEYS];
10731 Oid conppeqop[INDEX_MAX_KEYS];
10732 Oid conffeqop[INDEX_MAX_KEYS];
10733 int numfkdelsetcols;
10734 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10735 Constraint *fkconstraint;
10736 bool attached;
10737 Oid indexOid;
10738 Oid constrOid;
10739 ObjectAddress address,
10740 referenced;
10741 ListCell *lc;
10742 Oid insertTriggerOid,
10743 updateTriggerOid;
10745 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
10746 if (!HeapTupleIsValid(tuple))
10747 elog(ERROR, "cache lookup failed for constraint %u",
10748 parentConstrOid);
10749 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10751 /* Don't clone constraints whose parents are being cloned */
10752 if (list_member_oid(clone, constrForm->conparentid))
10754 ReleaseSysCache(tuple);
10755 continue;
10759 * Need to prevent concurrent deletions. If pkrel is a partitioned
10760 * relation, that means to lock all partitions.
10762 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10763 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10764 (void) find_all_inheritors(RelationGetRelid(pkrel),
10765 ShareRowExclusiveLock, NULL);
10767 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
10768 conpfeqop, conppeqop, conffeqop,
10769 &numfkdelsetcols, confdelsetcols);
10770 for (int i = 0; i < numfks; i++)
10771 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
10774 * Get the "check" triggers belonging to the constraint to pass as
10775 * parent OIDs for similar triggers that will be created on the
10776 * partition in addFkRecurseReferencing(). They are also passed to
10777 * tryAttachPartitionForeignKey() below to simply assign as parents to
10778 * the partition's existing "check" triggers, that is, if the
10779 * corresponding constraints is deemed attachable to the parent
10780 * constraint.
10782 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10783 constrForm->confrelid, constrForm->conrelid,
10784 &insertTriggerOid, &updateTriggerOid);
10787 * Before creating a new constraint, see whether any existing FKs are
10788 * fit for the purpose. If one is, attach the parent constraint to
10789 * it, and don't clone anything. This way we avoid the expensive
10790 * verification step and don't end up with a duplicate FK, and we
10791 * don't need to recurse to partitions for this constraint.
10793 attached = false;
10794 foreach(lc, partFKs)
10796 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
10798 if (tryAttachPartitionForeignKey(fk,
10799 RelationGetRelid(partRel),
10800 parentConstrOid,
10801 numfks,
10802 mapped_conkey,
10803 confkey,
10804 conpfeqop,
10805 insertTriggerOid,
10806 updateTriggerOid,
10807 trigrel))
10809 attached = true;
10810 table_close(pkrel, NoLock);
10811 break;
10814 if (attached)
10816 ReleaseSysCache(tuple);
10817 continue;
10820 /* No dice. Set up to create our own constraint */
10821 fkconstraint = makeNode(Constraint);
10822 fkconstraint->contype = CONSTRAINT_FOREIGN;
10823 /* ->conname determined below */
10824 fkconstraint->deferrable = constrForm->condeferrable;
10825 fkconstraint->initdeferred = constrForm->condeferred;
10826 fkconstraint->location = -1;
10827 fkconstraint->pktable = NULL;
10828 /* ->fk_attrs determined below */
10829 fkconstraint->pk_attrs = NIL;
10830 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10831 fkconstraint->fk_upd_action = constrForm->confupdtype;
10832 fkconstraint->fk_del_action = constrForm->confdeltype;
10833 fkconstraint->fk_del_set_cols = NIL;
10834 fkconstraint->old_conpfeqop = NIL;
10835 fkconstraint->old_pktable_oid = InvalidOid;
10836 fkconstraint->skip_validation = false;
10837 fkconstraint->initially_valid = true;
10838 for (int i = 0; i < numfks; i++)
10840 Form_pg_attribute att;
10842 att = TupleDescAttr(RelationGetDescr(partRel),
10843 mapped_conkey[i] - 1);
10844 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10845 makeString(NameStr(att->attname)));
10847 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10848 RelationGetRelid(partRel),
10849 NameStr(constrForm->conname)))
10850 fkconstraint->conname =
10851 ChooseConstraintName(RelationGetRelationName(partRel),
10852 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10853 "fkey",
10854 RelationGetNamespace(partRel), NIL);
10855 else
10856 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10858 indexOid = constrForm->conindid;
10859 constrOid =
10860 CreateConstraintEntry(fkconstraint->conname,
10861 constrForm->connamespace,
10862 CONSTRAINT_FOREIGN,
10863 fkconstraint->deferrable,
10864 fkconstraint->initdeferred,
10865 constrForm->convalidated,
10866 parentConstrOid,
10867 RelationGetRelid(partRel),
10868 mapped_conkey,
10869 numfks,
10870 numfks,
10871 InvalidOid, /* not a domain constraint */
10872 indexOid,
10873 constrForm->confrelid, /* same foreign rel */
10874 confkey,
10875 conpfeqop,
10876 conppeqop,
10877 conffeqop,
10878 numfks,
10879 fkconstraint->fk_upd_action,
10880 fkconstraint->fk_del_action,
10881 confdelsetcols,
10882 numfkdelsetcols,
10883 fkconstraint->fk_matchtype,
10884 NULL,
10885 NULL,
10886 NULL,
10887 false, /* islocal */
10888 1, /* inhcount */
10889 false, /* conNoInherit */
10890 true);
10892 /* Set up partition dependencies for the new constraint */
10893 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10894 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10895 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10896 ObjectAddressSet(referenced, RelationRelationId,
10897 RelationGetRelid(partRel));
10898 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10900 /* Done with the cloned constraint's tuple */
10901 ReleaseSysCache(tuple);
10903 /* Make all this visible before recursing */
10904 CommandCounterIncrement();
10906 addFkRecurseReferencing(wqueue,
10907 fkconstraint,
10908 partRel,
10909 pkrel,
10910 indexOid,
10911 constrOid,
10912 numfks,
10913 confkey,
10914 mapped_conkey,
10915 conpfeqop,
10916 conppeqop,
10917 conffeqop,
10918 numfkdelsetcols,
10919 confdelsetcols,
10920 false, /* no old check exists */
10921 AccessExclusiveLock,
10922 insertTriggerOid,
10923 updateTriggerOid);
10924 table_close(pkrel, NoLock);
10927 table_close(trigrel, RowExclusiveLock);
10931 * When the parent of a partition receives [the referencing side of] a foreign
10932 * key, we must propagate that foreign key to the partition. However, the
10933 * partition might already have an equivalent foreign key; this routine
10934 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10935 * by the other parameters. If they are equivalent, create the link between
10936 * the two constraints and return true.
10938 * If the given FK does not match the one defined by rest of the params,
10939 * return false.
10941 static bool
10942 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
10943 Oid partRelid,
10944 Oid parentConstrOid,
10945 int numfks,
10946 AttrNumber *mapped_conkey,
10947 AttrNumber *confkey,
10948 Oid *conpfeqop,
10949 Oid parentInsTrigger,
10950 Oid parentUpdTrigger,
10951 Relation trigrel)
10953 HeapTuple parentConstrTup;
10954 Form_pg_constraint parentConstr;
10955 HeapTuple partcontup;
10956 Form_pg_constraint partConstr;
10957 ScanKeyData key;
10958 SysScanDesc scan;
10959 HeapTuple trigtup;
10960 Oid insertTriggerOid,
10961 updateTriggerOid;
10963 parentConstrTup = SearchSysCache1(CONSTROID,
10964 ObjectIdGetDatum(parentConstrOid));
10965 if (!HeapTupleIsValid(parentConstrTup))
10966 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
10967 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
10970 * Do some quick & easy initial checks. If any of these fail, we cannot
10971 * use this constraint.
10973 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
10975 ReleaseSysCache(parentConstrTup);
10976 return false;
10978 for (int i = 0; i < numfks; i++)
10980 if (fk->conkey[i] != mapped_conkey[i] ||
10981 fk->confkey[i] != confkey[i] ||
10982 fk->conpfeqop[i] != conpfeqop[i])
10984 ReleaseSysCache(parentConstrTup);
10985 return false;
10990 * Looks good so far; do some more extensive checks. Presumably the check
10991 * for 'convalidated' could be dropped, since we don't really care about
10992 * that, but let's be careful for now.
10994 partcontup = SearchSysCache1(CONSTROID,
10995 ObjectIdGetDatum(fk->conoid));
10996 if (!HeapTupleIsValid(partcontup))
10997 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
10998 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
10999 if (OidIsValid(partConstr->conparentid) ||
11000 !partConstr->convalidated ||
11001 partConstr->condeferrable != parentConstr->condeferrable ||
11002 partConstr->condeferred != parentConstr->condeferred ||
11003 partConstr->confupdtype != parentConstr->confupdtype ||
11004 partConstr->confdeltype != parentConstr->confdeltype ||
11005 partConstr->confmatchtype != parentConstr->confmatchtype)
11007 ReleaseSysCache(parentConstrTup);
11008 ReleaseSysCache(partcontup);
11009 return false;
11012 ReleaseSysCache(partcontup);
11013 ReleaseSysCache(parentConstrTup);
11016 * Looks good! Attach this constraint. The action triggers in the new
11017 * partition become redundant -- the parent table already has equivalent
11018 * ones, and those will be able to reach the partition. Remove the ones
11019 * in the partition. We identify them because they have our constraint
11020 * OID, as well as being on the referenced rel.
11022 ScanKeyInit(&key,
11023 Anum_pg_trigger_tgconstraint,
11024 BTEqualStrategyNumber, F_OIDEQ,
11025 ObjectIdGetDatum(fk->conoid));
11026 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11027 NULL, 1, &key);
11028 while ((trigtup = systable_getnext(scan)) != NULL)
11030 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11031 ObjectAddress trigger;
11033 if (trgform->tgconstrrelid != fk->conrelid)
11034 continue;
11035 if (trgform->tgrelid != fk->confrelid)
11036 continue;
11039 * The constraint is originally set up to contain this trigger as an
11040 * implementation object, so there's a dependency record that links
11041 * the two; however, since the trigger is no longer needed, we remove
11042 * the dependency link in order to be able to drop the trigger while
11043 * keeping the constraint intact.
11045 deleteDependencyRecordsFor(TriggerRelationId,
11046 trgform->oid,
11047 false);
11048 /* make dependency deletion visible to performDeletion */
11049 CommandCounterIncrement();
11050 ObjectAddressSet(trigger, TriggerRelationId,
11051 trgform->oid);
11052 performDeletion(&trigger, DROP_RESTRICT, 0);
11053 /* make trigger drop visible, in case the loop iterates */
11054 CommandCounterIncrement();
11057 systable_endscan(scan);
11059 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11062 * Like the constraint, attach partition's "check" triggers to the
11063 * corresponding parent triggers.
11065 GetForeignKeyCheckTriggers(trigrel,
11066 fk->conoid, fk->confrelid, fk->conrelid,
11067 &insertTriggerOid, &updateTriggerOid);
11068 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11069 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11070 partRelid);
11071 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11072 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11073 partRelid);
11075 CommandCounterIncrement();
11076 return true;
11080 * GetForeignKeyActionTriggers
11081 * Returns delete and update "action" triggers of the given relation
11082 * belonging to the given constraint
11084 static void
11085 GetForeignKeyActionTriggers(Relation trigrel,
11086 Oid conoid, Oid confrelid, Oid conrelid,
11087 Oid *deleteTriggerOid,
11088 Oid *updateTriggerOid)
11090 ScanKeyData key;
11091 SysScanDesc scan;
11092 HeapTuple trigtup;
11094 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11095 ScanKeyInit(&key,
11096 Anum_pg_trigger_tgconstraint,
11097 BTEqualStrategyNumber, F_OIDEQ,
11098 ObjectIdGetDatum(conoid));
11100 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11101 NULL, 1, &key);
11102 while ((trigtup = systable_getnext(scan)) != NULL)
11104 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11106 if (trgform->tgconstrrelid != conrelid)
11107 continue;
11108 if (trgform->tgrelid != confrelid)
11109 continue;
11110 /* Only ever look at "action" triggers on the PK side. */
11111 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11112 continue;
11113 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11115 Assert(*deleteTriggerOid == InvalidOid);
11116 *deleteTriggerOid = trgform->oid;
11118 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11120 Assert(*updateTriggerOid == InvalidOid);
11121 *updateTriggerOid = trgform->oid;
11123 #ifndef USE_ASSERT_CHECKING
11124 /* In an assert-enabled build, continue looking to find duplicates */
11125 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11126 break;
11127 #endif
11130 if (!OidIsValid(*deleteTriggerOid))
11131 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11132 conoid);
11133 if (!OidIsValid(*updateTriggerOid))
11134 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11135 conoid);
11137 systable_endscan(scan);
11141 * GetForeignKeyCheckTriggers
11142 * Returns insert and update "check" triggers of the given relation
11143 * belonging to the given constraint
11145 static void
11146 GetForeignKeyCheckTriggers(Relation trigrel,
11147 Oid conoid, Oid confrelid, Oid conrelid,
11148 Oid *insertTriggerOid,
11149 Oid *updateTriggerOid)
11151 ScanKeyData key;
11152 SysScanDesc scan;
11153 HeapTuple trigtup;
11155 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11156 ScanKeyInit(&key,
11157 Anum_pg_trigger_tgconstraint,
11158 BTEqualStrategyNumber, F_OIDEQ,
11159 ObjectIdGetDatum(conoid));
11161 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11162 NULL, 1, &key);
11163 while ((trigtup = systable_getnext(scan)) != NULL)
11165 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11167 if (trgform->tgconstrrelid != confrelid)
11168 continue;
11169 if (trgform->tgrelid != conrelid)
11170 continue;
11171 /* Only ever look at "check" triggers on the FK side. */
11172 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11173 continue;
11174 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11176 Assert(*insertTriggerOid == InvalidOid);
11177 *insertTriggerOid = trgform->oid;
11179 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11181 Assert(*updateTriggerOid == InvalidOid);
11182 *updateTriggerOid = trgform->oid;
11184 #ifndef USE_ASSERT_CHECKING
11185 /* In an assert-enabled build, continue looking to find duplicates. */
11186 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11187 break;
11188 #endif
11191 if (!OidIsValid(*insertTriggerOid))
11192 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11193 conoid);
11194 if (!OidIsValid(*updateTriggerOid))
11195 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11196 conoid);
11198 systable_endscan(scan);
11202 * ALTER TABLE ALTER CONSTRAINT
11204 * Update the attributes of a constraint.
11206 * Currently only works for Foreign Key constraints.
11208 * If the constraint is modified, returns its address; otherwise, return
11209 * InvalidObjectAddress.
11211 static ObjectAddress
11212 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11213 bool recursing, LOCKMODE lockmode)
11215 Constraint *cmdcon;
11216 Relation conrel;
11217 Relation tgrel;
11218 SysScanDesc scan;
11219 ScanKeyData skey[3];
11220 HeapTuple contuple;
11221 Form_pg_constraint currcon;
11222 ObjectAddress address;
11223 List *otherrelids = NIL;
11224 ListCell *lc;
11226 cmdcon = castNode(Constraint, cmd->def);
11228 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11229 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11232 * Find and check the target constraint
11234 ScanKeyInit(&skey[0],
11235 Anum_pg_constraint_conrelid,
11236 BTEqualStrategyNumber, F_OIDEQ,
11237 ObjectIdGetDatum(RelationGetRelid(rel)));
11238 ScanKeyInit(&skey[1],
11239 Anum_pg_constraint_contypid,
11240 BTEqualStrategyNumber, F_OIDEQ,
11241 ObjectIdGetDatum(InvalidOid));
11242 ScanKeyInit(&skey[2],
11243 Anum_pg_constraint_conname,
11244 BTEqualStrategyNumber, F_NAMEEQ,
11245 CStringGetDatum(cmdcon->conname));
11246 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11247 true, NULL, 3, skey);
11249 /* There can be at most one matching row */
11250 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11251 ereport(ERROR,
11252 (errcode(ERRCODE_UNDEFINED_OBJECT),
11253 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11254 cmdcon->conname, RelationGetRelationName(rel))));
11256 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11257 if (currcon->contype != CONSTRAINT_FOREIGN)
11258 ereport(ERROR,
11259 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11260 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11261 cmdcon->conname, RelationGetRelationName(rel))));
11264 * If it's not the topmost constraint, raise an error.
11266 * Altering a non-topmost constraint leaves some triggers untouched, since
11267 * they are not directly connected to this constraint; also, pg_dump would
11268 * ignore the deferrability status of the individual constraint, since it
11269 * only dumps topmost constraints. Avoid these problems by refusing this
11270 * operation and telling the user to alter the parent constraint instead.
11272 if (OidIsValid(currcon->conparentid))
11274 HeapTuple tp;
11275 Oid parent = currcon->conparentid;
11276 char *ancestorname = NULL;
11277 char *ancestortable = NULL;
11279 /* Loop to find the topmost constraint */
11280 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11282 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11284 /* If no parent, this is the constraint we want */
11285 if (!OidIsValid(contup->conparentid))
11287 ancestorname = pstrdup(NameStr(contup->conname));
11288 ancestortable = get_rel_name(contup->conrelid);
11289 ReleaseSysCache(tp);
11290 break;
11293 parent = contup->conparentid;
11294 ReleaseSysCache(tp);
11297 ereport(ERROR,
11298 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11299 cmdcon->conname, RelationGetRelationName(rel)),
11300 ancestorname && ancestortable ?
11301 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11302 cmdcon->conname, ancestorname, ancestortable) : 0,
11303 errhint("You may alter the constraint it derives from instead.")));
11307 * Do the actual catalog work. We can skip changing if already in the
11308 * desired state, but not if a partitioned table: partitions need to be
11309 * processed regardless, in case they had the constraint locally changed.
11311 address = InvalidObjectAddress;
11312 if (currcon->condeferrable != cmdcon->deferrable ||
11313 currcon->condeferred != cmdcon->initdeferred ||
11314 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11316 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11317 &otherrelids, lockmode))
11318 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11322 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11323 * having the constraint itself; here we also invalidate for relations
11324 * that have any triggers that are part of the constraint.
11326 foreach(lc, otherrelids)
11327 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11329 systable_endscan(scan);
11331 table_close(tgrel, RowExclusiveLock);
11332 table_close(conrel, RowExclusiveLock);
11334 return address;
11338 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11339 * constraint is altered.
11341 * *otherrelids is appended OIDs of relations containing affected triggers.
11343 * Note that we must recurse even when the values are correct, in case
11344 * indirect descendants have had their constraints altered locally.
11345 * (This could be avoided if we forbade altering constraints in partitions
11346 * but existing releases don't do that.)
11348 static bool
11349 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11350 Relation rel, HeapTuple contuple, List **otherrelids,
11351 LOCKMODE lockmode)
11353 Form_pg_constraint currcon;
11354 Oid conoid;
11355 Oid refrelid;
11356 bool changed = false;
11358 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11359 conoid = currcon->oid;
11360 refrelid = currcon->confrelid;
11363 * Update pg_constraint with the flags from cmdcon.
11365 * If called to modify a constraint that's already in the desired state,
11366 * silently do nothing.
11368 if (currcon->condeferrable != cmdcon->deferrable ||
11369 currcon->condeferred != cmdcon->initdeferred)
11371 HeapTuple copyTuple;
11372 Form_pg_constraint copy_con;
11373 HeapTuple tgtuple;
11374 ScanKeyData tgkey;
11375 SysScanDesc tgscan;
11377 copyTuple = heap_copytuple(contuple);
11378 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11379 copy_con->condeferrable = cmdcon->deferrable;
11380 copy_con->condeferred = cmdcon->initdeferred;
11381 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11383 InvokeObjectPostAlterHook(ConstraintRelationId,
11384 conoid, 0);
11386 heap_freetuple(copyTuple);
11387 changed = true;
11389 /* Make new constraint flags visible to others */
11390 CacheInvalidateRelcache(rel);
11393 * Now we need to update the multiple entries in pg_trigger that
11394 * implement the constraint.
11396 ScanKeyInit(&tgkey,
11397 Anum_pg_trigger_tgconstraint,
11398 BTEqualStrategyNumber, F_OIDEQ,
11399 ObjectIdGetDatum(conoid));
11400 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11401 NULL, 1, &tgkey);
11402 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11404 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11405 Form_pg_trigger copy_tg;
11406 HeapTuple tgCopyTuple;
11409 * Remember OIDs of other relation(s) involved in FK constraint.
11410 * (Note: it's likely that we could skip forcing a relcache inval
11411 * for other rels that don't have a trigger whose properties
11412 * change, but let's be conservative.)
11414 if (tgform->tgrelid != RelationGetRelid(rel))
11415 *otherrelids = list_append_unique_oid(*otherrelids,
11416 tgform->tgrelid);
11419 * Update deferrability of RI_FKey_noaction_del,
11420 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11421 * triggers, but not others; see createForeignKeyActionTriggers
11422 * and CreateFKCheckTrigger.
11424 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11425 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11426 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11427 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11428 continue;
11430 tgCopyTuple = heap_copytuple(tgtuple);
11431 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11433 copy_tg->tgdeferrable = cmdcon->deferrable;
11434 copy_tg->tginitdeferred = cmdcon->initdeferred;
11435 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11437 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11439 heap_freetuple(tgCopyTuple);
11442 systable_endscan(tgscan);
11446 * If the table at either end of the constraint is partitioned, we need to
11447 * recurse and handle every constraint that is a child of this one.
11449 * (This assumes that the recurse flag is forcibly set for partitioned
11450 * tables, and not set for legacy inheritance, though we don't check for
11451 * that here.)
11453 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11454 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11456 ScanKeyData pkey;
11457 SysScanDesc pscan;
11458 HeapTuple childtup;
11460 ScanKeyInit(&pkey,
11461 Anum_pg_constraint_conparentid,
11462 BTEqualStrategyNumber, F_OIDEQ,
11463 ObjectIdGetDatum(conoid));
11465 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11466 true, NULL, 1, &pkey);
11468 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11470 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11471 Relation childrel;
11473 childrel = table_open(childcon->conrelid, lockmode);
11474 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11475 otherrelids, lockmode);
11476 table_close(childrel, NoLock);
11479 systable_endscan(pscan);
11482 return changed;
11486 * ALTER TABLE VALIDATE CONSTRAINT
11488 * XXX The reason we handle recursion here rather than at Phase 1 is because
11489 * there's no good way to skip recursing when handling foreign keys: there is
11490 * no need to lock children in that case, yet we wouldn't be able to avoid
11491 * doing so at that level.
11493 * Return value is the address of the validated constraint. If the constraint
11494 * was already validated, InvalidObjectAddress is returned.
11496 static ObjectAddress
11497 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11498 bool recurse, bool recursing, LOCKMODE lockmode)
11500 Relation conrel;
11501 SysScanDesc scan;
11502 ScanKeyData skey[3];
11503 HeapTuple tuple;
11504 Form_pg_constraint con;
11505 ObjectAddress address;
11507 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11510 * Find and check the target constraint
11512 ScanKeyInit(&skey[0],
11513 Anum_pg_constraint_conrelid,
11514 BTEqualStrategyNumber, F_OIDEQ,
11515 ObjectIdGetDatum(RelationGetRelid(rel)));
11516 ScanKeyInit(&skey[1],
11517 Anum_pg_constraint_contypid,
11518 BTEqualStrategyNumber, F_OIDEQ,
11519 ObjectIdGetDatum(InvalidOid));
11520 ScanKeyInit(&skey[2],
11521 Anum_pg_constraint_conname,
11522 BTEqualStrategyNumber, F_NAMEEQ,
11523 CStringGetDatum(constrName));
11524 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11525 true, NULL, 3, skey);
11527 /* There can be at most one matching row */
11528 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11529 ereport(ERROR,
11530 (errcode(ERRCODE_UNDEFINED_OBJECT),
11531 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11532 constrName, RelationGetRelationName(rel))));
11534 con = (Form_pg_constraint) GETSTRUCT(tuple);
11535 if (con->contype != CONSTRAINT_FOREIGN &&
11536 con->contype != CONSTRAINT_CHECK)
11537 ereport(ERROR,
11538 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11539 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11540 constrName, RelationGetRelationName(rel))));
11542 if (!con->convalidated)
11544 AlteredTableInfo *tab;
11545 HeapTuple copyTuple;
11546 Form_pg_constraint copy_con;
11548 if (con->contype == CONSTRAINT_FOREIGN)
11550 NewConstraint *newcon;
11551 Constraint *fkconstraint;
11553 /* Queue validation for phase 3 */
11554 fkconstraint = makeNode(Constraint);
11555 /* for now this is all we need */
11556 fkconstraint->conname = constrName;
11558 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11559 newcon->name = constrName;
11560 newcon->contype = CONSTR_FOREIGN;
11561 newcon->refrelid = con->confrelid;
11562 newcon->refindid = con->conindid;
11563 newcon->conid = con->oid;
11564 newcon->qual = (Node *) fkconstraint;
11566 /* Find or create work queue entry for this table */
11567 tab = ATGetQueueEntry(wqueue, rel);
11568 tab->constraints = lappend(tab->constraints, newcon);
11571 * We disallow creating invalid foreign keys to or from
11572 * partitioned tables, so ignoring the recursion bit is okay.
11575 else if (con->contype == CONSTRAINT_CHECK)
11577 List *children = NIL;
11578 ListCell *child;
11579 NewConstraint *newcon;
11580 Datum val;
11581 char *conbin;
11584 * If we're recursing, the parent has already done this, so skip
11585 * it. Also, if the constraint is a NO INHERIT constraint, we
11586 * shouldn't try to look for it in the children.
11588 if (!recursing && !con->connoinherit)
11589 children = find_all_inheritors(RelationGetRelid(rel),
11590 lockmode, NULL);
11593 * For CHECK constraints, we must ensure that we only mark the
11594 * constraint as validated on the parent if it's already validated
11595 * on the children.
11597 * We recurse before validating on the parent, to reduce risk of
11598 * deadlocks.
11600 foreach(child, children)
11602 Oid childoid = lfirst_oid(child);
11603 Relation childrel;
11605 if (childoid == RelationGetRelid(rel))
11606 continue;
11609 * If we are told not to recurse, there had better not be any
11610 * child tables, because we can't mark the constraint on the
11611 * parent valid unless it is valid for all child tables.
11613 if (!recurse)
11614 ereport(ERROR,
11615 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11616 errmsg("constraint must be validated on child tables too")));
11618 /* find_all_inheritors already got lock */
11619 childrel = table_open(childoid, NoLock);
11621 ATExecValidateConstraint(wqueue, childrel, constrName, false,
11622 true, lockmode);
11623 table_close(childrel, NoLock);
11626 /* Queue validation for phase 3 */
11627 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11628 newcon->name = constrName;
11629 newcon->contype = CONSTR_CHECK;
11630 newcon->refrelid = InvalidOid;
11631 newcon->refindid = InvalidOid;
11632 newcon->conid = con->oid;
11634 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11635 Anum_pg_constraint_conbin);
11636 conbin = TextDatumGetCString(val);
11637 newcon->qual = (Node *) stringToNode(conbin);
11639 /* Find or create work queue entry for this table */
11640 tab = ATGetQueueEntry(wqueue, rel);
11641 tab->constraints = lappend(tab->constraints, newcon);
11644 * Invalidate relcache so that others see the new validated
11645 * constraint.
11647 CacheInvalidateRelcache(rel);
11651 * Now update the catalog, while we have the door open.
11653 copyTuple = heap_copytuple(tuple);
11654 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11655 copy_con->convalidated = true;
11656 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11658 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11660 heap_freetuple(copyTuple);
11662 ObjectAddressSet(address, ConstraintRelationId, con->oid);
11664 else
11665 address = InvalidObjectAddress; /* already validated */
11667 systable_endscan(scan);
11669 table_close(conrel, RowExclusiveLock);
11671 return address;
11676 * transformColumnNameList - transform list of column names
11678 * Lookup each name and return its attnum and, optionally, type OID
11680 * Note: the name of this function suggests that it's general-purpose,
11681 * but actually it's only used to look up names appearing in foreign-key
11682 * clauses. The error messages would need work to use it in other cases,
11683 * and perhaps the validity checks as well.
11685 static int
11686 transformColumnNameList(Oid relId, List *colList,
11687 int16 *attnums, Oid *atttypids)
11689 ListCell *l;
11690 int attnum;
11692 attnum = 0;
11693 foreach(l, colList)
11695 char *attname = strVal(lfirst(l));
11696 HeapTuple atttuple;
11697 Form_pg_attribute attform;
11699 atttuple = SearchSysCacheAttName(relId, attname);
11700 if (!HeapTupleIsValid(atttuple))
11701 ereport(ERROR,
11702 (errcode(ERRCODE_UNDEFINED_COLUMN),
11703 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11704 attname)));
11705 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
11706 if (attform->attnum < 0)
11707 ereport(ERROR,
11708 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11709 errmsg("system columns cannot be used in foreign keys")));
11710 if (attnum >= INDEX_MAX_KEYS)
11711 ereport(ERROR,
11712 (errcode(ERRCODE_TOO_MANY_COLUMNS),
11713 errmsg("cannot have more than %d keys in a foreign key",
11714 INDEX_MAX_KEYS)));
11715 attnums[attnum] = attform->attnum;
11716 if (atttypids != NULL)
11717 atttypids[attnum] = attform->atttypid;
11718 ReleaseSysCache(atttuple);
11719 attnum++;
11722 return attnum;
11726 * transformFkeyGetPrimaryKey -
11728 * Look up the names, attnums, and types of the primary key attributes
11729 * for the pkrel. Also return the index OID and index opclasses of the
11730 * index supporting the primary key.
11732 * All parameters except pkrel are output parameters. Also, the function
11733 * return value is the number of attributes in the primary key.
11735 * Used when the column list in the REFERENCES specification is omitted.
11737 static int
11738 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
11739 List **attnamelist,
11740 int16 *attnums, Oid *atttypids,
11741 Oid *opclasses)
11743 List *indexoidlist;
11744 ListCell *indexoidscan;
11745 HeapTuple indexTuple = NULL;
11746 Form_pg_index indexStruct = NULL;
11747 Datum indclassDatum;
11748 oidvector *indclass;
11749 int i;
11752 * Get the list of index OIDs for the table from the relcache, and look up
11753 * each one in the pg_index syscache until we find one marked primary key
11754 * (hopefully there isn't more than one such). Insist it's valid, too.
11756 *indexOid = InvalidOid;
11758 indexoidlist = RelationGetIndexList(pkrel);
11760 foreach(indexoidscan, indexoidlist)
11762 Oid indexoid = lfirst_oid(indexoidscan);
11764 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11765 if (!HeapTupleIsValid(indexTuple))
11766 elog(ERROR, "cache lookup failed for index %u", indexoid);
11767 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11768 if (indexStruct->indisprimary && indexStruct->indisvalid)
11771 * Refuse to use a deferrable primary key. This is per SQL spec,
11772 * and there would be a lot of interesting semantic problems if we
11773 * tried to allow it.
11775 if (!indexStruct->indimmediate)
11776 ereport(ERROR,
11777 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11778 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11779 RelationGetRelationName(pkrel))));
11781 *indexOid = indexoid;
11782 break;
11784 ReleaseSysCache(indexTuple);
11787 list_free(indexoidlist);
11790 * Check that we found it
11792 if (!OidIsValid(*indexOid))
11793 ereport(ERROR,
11794 (errcode(ERRCODE_UNDEFINED_OBJECT),
11795 errmsg("there is no primary key for referenced table \"%s\"",
11796 RelationGetRelationName(pkrel))));
11798 /* Must get indclass the hard way */
11799 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11800 Anum_pg_index_indclass);
11801 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11804 * Now build the list of PK attributes from the indkey definition (we
11805 * assume a primary key cannot have expressional elements)
11807 *attnamelist = NIL;
11808 for (i = 0; i < indexStruct->indnkeyatts; i++)
11810 int pkattno = indexStruct->indkey.values[i];
11812 attnums[i] = pkattno;
11813 atttypids[i] = attnumTypeId(pkrel, pkattno);
11814 opclasses[i] = indclass->values[i];
11815 *attnamelist = lappend(*attnamelist,
11816 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11819 ReleaseSysCache(indexTuple);
11821 return i;
11825 * transformFkeyCheckAttrs -
11827 * Make sure that the attributes of a referenced table belong to a unique
11828 * (or primary key) constraint. Return the OID of the index supporting
11829 * the constraint, as well as the opclasses associated with the index
11830 * columns.
11832 static Oid
11833 transformFkeyCheckAttrs(Relation pkrel,
11834 int numattrs, int16 *attnums,
11835 Oid *opclasses) /* output parameter */
11837 Oid indexoid = InvalidOid;
11838 bool found = false;
11839 bool found_deferrable = false;
11840 List *indexoidlist;
11841 ListCell *indexoidscan;
11842 int i,
11846 * Reject duplicate appearances of columns in the referenced-columns list.
11847 * Such a case is forbidden by the SQL standard, and even if we thought it
11848 * useful to allow it, there would be ambiguity about how to match the
11849 * list to unique indexes (in particular, it'd be unclear which index
11850 * opclass goes with which FK column).
11852 for (i = 0; i < numattrs; i++)
11854 for (j = i + 1; j < numattrs; j++)
11856 if (attnums[i] == attnums[j])
11857 ereport(ERROR,
11858 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11859 errmsg("foreign key referenced-columns list must not contain duplicates")));
11864 * Get the list of index OIDs for the table from the relcache, and look up
11865 * each one in the pg_index syscache, and match unique indexes to the list
11866 * of attnums we are given.
11868 indexoidlist = RelationGetIndexList(pkrel);
11870 foreach(indexoidscan, indexoidlist)
11872 HeapTuple indexTuple;
11873 Form_pg_index indexStruct;
11875 indexoid = lfirst_oid(indexoidscan);
11876 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11877 if (!HeapTupleIsValid(indexTuple))
11878 elog(ERROR, "cache lookup failed for index %u", indexoid);
11879 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11882 * Must have the right number of columns; must be unique and not a
11883 * partial index; forget it if there are any expressions, too. Invalid
11884 * indexes are out as well.
11886 if (indexStruct->indnkeyatts == numattrs &&
11887 indexStruct->indisunique &&
11888 indexStruct->indisvalid &&
11889 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11890 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
11892 Datum indclassDatum;
11893 oidvector *indclass;
11895 /* Must get indclass the hard way */
11896 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11897 Anum_pg_index_indclass);
11898 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11901 * The given attnum list may match the index columns in any order.
11902 * Check for a match, and extract the appropriate opclasses while
11903 * we're at it.
11905 * We know that attnums[] is duplicate-free per the test at the
11906 * start of this function, and we checked above that the number of
11907 * index columns agrees, so if we find a match for each attnums[]
11908 * entry then we must have a one-to-one match in some order.
11910 for (i = 0; i < numattrs; i++)
11912 found = false;
11913 for (j = 0; j < numattrs; j++)
11915 if (attnums[i] == indexStruct->indkey.values[j])
11917 opclasses[i] = indclass->values[j];
11918 found = true;
11919 break;
11922 if (!found)
11923 break;
11927 * Refuse to use a deferrable unique/primary key. This is per SQL
11928 * spec, and there would be a lot of interesting semantic problems
11929 * if we tried to allow it.
11931 if (found && !indexStruct->indimmediate)
11934 * Remember that we found an otherwise matching index, so that
11935 * we can generate a more appropriate error message.
11937 found_deferrable = true;
11938 found = false;
11941 ReleaseSysCache(indexTuple);
11942 if (found)
11943 break;
11946 if (!found)
11948 if (found_deferrable)
11949 ereport(ERROR,
11950 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11951 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
11952 RelationGetRelationName(pkrel))));
11953 else
11954 ereport(ERROR,
11955 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11956 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
11957 RelationGetRelationName(pkrel))));
11960 list_free(indexoidlist);
11962 return indexoid;
11966 * findFkeyCast -
11968 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
11969 * Caller has equal regard for binary coercibility and for an exact match.
11971 static CoercionPathType
11972 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
11974 CoercionPathType ret;
11976 if (targetTypeId == sourceTypeId)
11978 ret = COERCION_PATH_RELABELTYPE;
11979 *funcid = InvalidOid;
11981 else
11983 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
11984 COERCION_IMPLICIT, funcid);
11985 if (ret == COERCION_PATH_NONE)
11986 /* A previously-relied-upon cast is now gone. */
11987 elog(ERROR, "could not find cast from %u to %u",
11988 sourceTypeId, targetTypeId);
11991 return ret;
11995 * Permissions checks on the referenced table for ADD FOREIGN KEY
11997 * Note: we have already checked that the user owns the referencing table,
11998 * else we'd have failed much earlier; no additional checks are needed for it.
12000 static void
12001 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12003 Oid roleid = GetUserId();
12004 AclResult aclresult;
12005 int i;
12007 /* Okay if we have relation-level REFERENCES permission */
12008 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12009 ACL_REFERENCES);
12010 if (aclresult == ACLCHECK_OK)
12011 return;
12012 /* Else we must have REFERENCES on each column */
12013 for (i = 0; i < natts; i++)
12015 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12016 roleid, ACL_REFERENCES);
12017 if (aclresult != ACLCHECK_OK)
12018 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12019 RelationGetRelationName(rel));
12024 * Scan the existing rows in a table to verify they meet a proposed FK
12025 * constraint.
12027 * Caller must have opened and locked both relations appropriately.
12029 static void
12030 validateForeignKeyConstraint(char *conname,
12031 Relation rel,
12032 Relation pkrel,
12033 Oid pkindOid,
12034 Oid constraintOid)
12036 TupleTableSlot *slot;
12037 TableScanDesc scan;
12038 Trigger trig = {0};
12039 Snapshot snapshot;
12040 MemoryContext oldcxt;
12041 MemoryContext perTupCxt;
12043 ereport(DEBUG1,
12044 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12047 * Build a trigger call structure; we'll need it either way.
12049 trig.tgoid = InvalidOid;
12050 trig.tgname = conname;
12051 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12052 trig.tgisinternal = true;
12053 trig.tgconstrrelid = RelationGetRelid(pkrel);
12054 trig.tgconstrindid = pkindOid;
12055 trig.tgconstraint = constraintOid;
12056 trig.tgdeferrable = false;
12057 trig.tginitdeferred = false;
12058 /* we needn't fill in remaining fields */
12061 * See if we can do it with a single LEFT JOIN query. A false result
12062 * indicates we must proceed with the fire-the-trigger method.
12064 if (RI_Initial_Check(&trig, rel, pkrel))
12065 return;
12068 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12069 * if that tuple had just been inserted. If any of those fail, it should
12070 * ereport(ERROR) and that's that.
12072 snapshot = RegisterSnapshot(GetLatestSnapshot());
12073 slot = table_slot_create(rel, NULL);
12074 scan = table_beginscan(rel, snapshot, 0, NULL);
12076 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12077 "validateForeignKeyConstraint",
12078 ALLOCSET_SMALL_SIZES);
12079 oldcxt = MemoryContextSwitchTo(perTupCxt);
12081 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12083 LOCAL_FCINFO(fcinfo, 0);
12084 TriggerData trigdata = {0};
12086 CHECK_FOR_INTERRUPTS();
12089 * Make a call to the trigger function
12091 * No parameters are passed, but we do set a context
12093 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12096 * We assume RI_FKey_check_ins won't look at flinfo...
12098 trigdata.type = T_TriggerData;
12099 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12100 trigdata.tg_relation = rel;
12101 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12102 trigdata.tg_trigslot = slot;
12103 trigdata.tg_trigger = &trig;
12105 fcinfo->context = (Node *) &trigdata;
12107 RI_FKey_check_ins(fcinfo);
12109 MemoryContextReset(perTupCxt);
12112 MemoryContextSwitchTo(oldcxt);
12113 MemoryContextDelete(perTupCxt);
12114 table_endscan(scan);
12115 UnregisterSnapshot(snapshot);
12116 ExecDropSingleTupleTableSlot(slot);
12120 * CreateFKCheckTrigger
12121 * Creates the insert (on_insert=true) or update "check" trigger that
12122 * implements a given foreign key
12124 * Returns the OID of the so created trigger.
12126 static Oid
12127 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12128 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12129 bool on_insert)
12131 ObjectAddress trigAddress;
12132 CreateTrigStmt *fk_trigger;
12135 * Note: for a self-referential FK (referencing and referenced tables are
12136 * the same), it is important that the ON UPDATE action fires before the
12137 * CHECK action, since both triggers will fire on the same row during an
12138 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12139 * state of the row. Triggers fire in name order, so we ensure this by
12140 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12141 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12143 fk_trigger = makeNode(CreateTrigStmt);
12144 fk_trigger->replace = false;
12145 fk_trigger->isconstraint = true;
12146 fk_trigger->trigname = "RI_ConstraintTrigger_c";
12147 fk_trigger->relation = NULL;
12149 /* Either ON INSERT or ON UPDATE */
12150 if (on_insert)
12152 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12153 fk_trigger->events = TRIGGER_TYPE_INSERT;
12155 else
12157 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12158 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12161 fk_trigger->args = NIL;
12162 fk_trigger->row = true;
12163 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12164 fk_trigger->columns = NIL;
12165 fk_trigger->whenClause = NULL;
12166 fk_trigger->transitionRels = NIL;
12167 fk_trigger->deferrable = fkconstraint->deferrable;
12168 fk_trigger->initdeferred = fkconstraint->initdeferred;
12169 fk_trigger->constrrel = NULL;
12171 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12172 constraintOid, indexOid, InvalidOid,
12173 parentTrigOid, NULL, true, false);
12175 /* Make changes-so-far visible */
12176 CommandCounterIncrement();
12178 return trigAddress.objectId;
12182 * createForeignKeyActionTriggers
12183 * Create the referenced-side "action" triggers that implement a foreign
12184 * key.
12186 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12187 * *updateTrigOid.
12189 static void
12190 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12191 Oid constraintOid, Oid indexOid,
12192 Oid parentDelTrigger, Oid parentUpdTrigger,
12193 Oid *deleteTrigOid, Oid *updateTrigOid)
12195 CreateTrigStmt *fk_trigger;
12196 ObjectAddress trigAddress;
12199 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12200 * DELETE action on the referenced table.
12202 fk_trigger = makeNode(CreateTrigStmt);
12203 fk_trigger->replace = false;
12204 fk_trigger->isconstraint = true;
12205 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12206 fk_trigger->relation = NULL;
12207 fk_trigger->args = NIL;
12208 fk_trigger->row = true;
12209 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12210 fk_trigger->events = TRIGGER_TYPE_DELETE;
12211 fk_trigger->columns = NIL;
12212 fk_trigger->whenClause = NULL;
12213 fk_trigger->transitionRels = NIL;
12214 fk_trigger->constrrel = NULL;
12215 switch (fkconstraint->fk_del_action)
12217 case FKCONSTR_ACTION_NOACTION:
12218 fk_trigger->deferrable = fkconstraint->deferrable;
12219 fk_trigger->initdeferred = fkconstraint->initdeferred;
12220 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12221 break;
12222 case FKCONSTR_ACTION_RESTRICT:
12223 fk_trigger->deferrable = false;
12224 fk_trigger->initdeferred = false;
12225 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12226 break;
12227 case FKCONSTR_ACTION_CASCADE:
12228 fk_trigger->deferrable = false;
12229 fk_trigger->initdeferred = false;
12230 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12231 break;
12232 case FKCONSTR_ACTION_SETNULL:
12233 fk_trigger->deferrable = false;
12234 fk_trigger->initdeferred = false;
12235 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12236 break;
12237 case FKCONSTR_ACTION_SETDEFAULT:
12238 fk_trigger->deferrable = false;
12239 fk_trigger->initdeferred = false;
12240 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12241 break;
12242 default:
12243 elog(ERROR, "unrecognized FK action type: %d",
12244 (int) fkconstraint->fk_del_action);
12245 break;
12248 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12249 RelationGetRelid(rel),
12250 constraintOid, indexOid, InvalidOid,
12251 parentDelTrigger, NULL, true, false);
12252 if (deleteTrigOid)
12253 *deleteTrigOid = trigAddress.objectId;
12255 /* Make changes-so-far visible */
12256 CommandCounterIncrement();
12259 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12260 * UPDATE action on the referenced table.
12262 fk_trigger = makeNode(CreateTrigStmt);
12263 fk_trigger->replace = false;
12264 fk_trigger->isconstraint = true;
12265 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12266 fk_trigger->relation = NULL;
12267 fk_trigger->args = NIL;
12268 fk_trigger->row = true;
12269 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12270 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12271 fk_trigger->columns = NIL;
12272 fk_trigger->whenClause = NULL;
12273 fk_trigger->transitionRels = NIL;
12274 fk_trigger->constrrel = NULL;
12275 switch (fkconstraint->fk_upd_action)
12277 case FKCONSTR_ACTION_NOACTION:
12278 fk_trigger->deferrable = fkconstraint->deferrable;
12279 fk_trigger->initdeferred = fkconstraint->initdeferred;
12280 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12281 break;
12282 case FKCONSTR_ACTION_RESTRICT:
12283 fk_trigger->deferrable = false;
12284 fk_trigger->initdeferred = false;
12285 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12286 break;
12287 case FKCONSTR_ACTION_CASCADE:
12288 fk_trigger->deferrable = false;
12289 fk_trigger->initdeferred = false;
12290 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12291 break;
12292 case FKCONSTR_ACTION_SETNULL:
12293 fk_trigger->deferrable = false;
12294 fk_trigger->initdeferred = false;
12295 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12296 break;
12297 case FKCONSTR_ACTION_SETDEFAULT:
12298 fk_trigger->deferrable = false;
12299 fk_trigger->initdeferred = false;
12300 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12301 break;
12302 default:
12303 elog(ERROR, "unrecognized FK action type: %d",
12304 (int) fkconstraint->fk_upd_action);
12305 break;
12308 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12309 RelationGetRelid(rel),
12310 constraintOid, indexOid, InvalidOid,
12311 parentUpdTrigger, NULL, true, false);
12312 if (updateTrigOid)
12313 *updateTrigOid = trigAddress.objectId;
12317 * createForeignKeyCheckTriggers
12318 * Create the referencing-side "check" triggers that implement a foreign
12319 * key.
12321 * Returns the OIDs of the so created triggers in *insertTrigOid and
12322 * *updateTrigOid.
12324 static void
12325 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12326 Constraint *fkconstraint, Oid constraintOid,
12327 Oid indexOid,
12328 Oid parentInsTrigger, Oid parentUpdTrigger,
12329 Oid *insertTrigOid, Oid *updateTrigOid)
12331 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12332 constraintOid, indexOid,
12333 parentInsTrigger, true);
12334 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12335 constraintOid, indexOid,
12336 parentUpdTrigger, false);
12340 * ALTER TABLE DROP CONSTRAINT
12342 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12344 static void
12345 ATExecDropConstraint(Relation rel, const char *constrName,
12346 DropBehavior behavior, bool recurse,
12347 bool missing_ok, LOCKMODE lockmode)
12349 Relation conrel;
12350 SysScanDesc scan;
12351 ScanKeyData skey[3];
12352 HeapTuple tuple;
12353 bool found = false;
12355 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12358 * Find and drop the target constraint
12360 ScanKeyInit(&skey[0],
12361 Anum_pg_constraint_conrelid,
12362 BTEqualStrategyNumber, F_OIDEQ,
12363 ObjectIdGetDatum(RelationGetRelid(rel)));
12364 ScanKeyInit(&skey[1],
12365 Anum_pg_constraint_contypid,
12366 BTEqualStrategyNumber, F_OIDEQ,
12367 ObjectIdGetDatum(InvalidOid));
12368 ScanKeyInit(&skey[2],
12369 Anum_pg_constraint_conname,
12370 BTEqualStrategyNumber, F_NAMEEQ,
12371 CStringGetDatum(constrName));
12372 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12373 true, NULL, 3, skey);
12375 /* There can be at most one matching row */
12376 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12378 List *readyRels = NIL;
12380 dropconstraint_internal(rel, tuple, behavior, recurse, false,
12381 missing_ok, &readyRels, lockmode);
12382 found = true;
12385 systable_endscan(scan);
12387 if (!found)
12389 if (!missing_ok)
12390 ereport(ERROR,
12391 errcode(ERRCODE_UNDEFINED_OBJECT),
12392 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12393 constrName, RelationGetRelationName(rel)));
12394 else
12395 ereport(NOTICE,
12396 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12397 constrName, RelationGetRelationName(rel)));
12400 table_close(conrel, RowExclusiveLock);
12404 * Remove a constraint, using its pg_constraint tuple
12406 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12407 * DROP NOT NULL.
12409 * Returns the address of the constraint being removed.
12411 static ObjectAddress
12412 dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12413 bool recurse, bool recursing, bool missing_ok, List **readyRels,
12414 LOCKMODE lockmode)
12416 Relation conrel;
12417 Form_pg_constraint con;
12418 ObjectAddress conobj;
12419 List *children;
12420 ListCell *child;
12421 bool is_no_inherit_constraint = false;
12422 bool dropping_pk = false;
12423 char *constrName;
12424 List *unconstrained_cols = NIL;
12425 char *colname;
12427 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12428 return InvalidObjectAddress;
12429 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12431 /* At top level, permission check was done in ATPrepCmd, else do it */
12432 if (recursing)
12433 ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12435 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12437 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12438 constrName = NameStr(con->conname);
12440 /* Don't allow drop of inherited constraints */
12441 if (con->coninhcount > 0 && !recursing)
12442 ereport(ERROR,
12443 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12444 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12445 constrName, RelationGetRelationName(rel))));
12448 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
12449 * more checks and actions below, so obtain the list of columns that are
12450 * constrained by the constraint being dropped.
12452 if (con->contype == CONSTRAINT_NOTNULL)
12454 AttrNumber colnum = extractNotNullColumn(constraintTup);
12456 if (colnum != InvalidAttrNumber)
12457 unconstrained_cols = list_make1_int(colnum);
12459 else if (con->contype == CONSTRAINT_PRIMARY)
12461 Datum adatum;
12462 ArrayType *arr;
12463 int numkeys;
12464 bool isNull;
12465 int16 *attnums;
12467 dropping_pk = true;
12469 adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
12470 RelationGetDescr(conrel), &isNull);
12471 if (isNull)
12472 elog(ERROR, "null conkey for constraint %u", con->oid);
12473 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
12474 numkeys = ARR_DIMS(arr)[0];
12475 if (ARR_NDIM(arr) != 1 ||
12476 numkeys < 0 ||
12477 ARR_HASNULL(arr) ||
12478 ARR_ELEMTYPE(arr) != INT2OID)
12479 elog(ERROR, "conkey is not a 1-D smallint array");
12480 attnums = (int16 *) ARR_DATA_PTR(arr);
12482 for (int i = 0; i < numkeys; i++)
12483 unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
12486 is_no_inherit_constraint = con->connoinherit;
12489 * If it's a foreign-key constraint, we'd better lock the referenced table
12490 * and check that that's not in use, just as we've already done for the
12491 * constrained table (else we might, eg, be dropping a trigger that has
12492 * unfired events). But we can/must skip that in the self-referential
12493 * case.
12495 if (con->contype == CONSTRAINT_FOREIGN &&
12496 con->confrelid != RelationGetRelid(rel))
12498 Relation frel;
12500 /* Must match lock taken by RemoveTriggerById: */
12501 frel = table_open(con->confrelid, AccessExclusiveLock);
12502 CheckTableNotInUse(frel, "ALTER TABLE");
12503 table_close(frel, NoLock);
12507 * Perform the actual constraint deletion
12509 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12510 performDeletion(&conobj, behavior, 0);
12513 * If this was a NOT NULL or the primary key, the constrained columns must
12514 * have had pg_attribute.attnotnull set. See if we need to reset it, and
12515 * do so.
12517 if (unconstrained_cols)
12519 Relation attrel;
12520 Bitmapset *pkcols;
12521 Bitmapset *ircols;
12522 ListCell *lc;
12524 /* Make the above deletion visible */
12525 CommandCounterIncrement();
12527 attrel = table_open(AttributeRelationId, RowExclusiveLock);
12530 * We want to test columns for their presence in the primary key, but
12531 * only if we're not dropping it.
12533 pkcols = dropping_pk ? NULL :
12534 RelationGetIndexAttrBitmap(rel,
12535 INDEX_ATTR_BITMAP_PRIMARY_KEY);
12536 ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
12538 foreach(lc, unconstrained_cols)
12540 AttrNumber attnum = lfirst_int(lc);
12541 HeapTuple atttup;
12542 HeapTuple contup;
12543 Form_pg_attribute attForm;
12546 * Obtain pg_attribute tuple and verify conditions on it. We use
12547 * a copy we can scribble on.
12549 atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
12550 if (!HeapTupleIsValid(atttup))
12551 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
12552 attnum, RelationGetRelid(rel));
12553 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
12556 * Since the above deletion has been made visible, we can now
12557 * search for any remaining constraints on this column (or these
12558 * columns, in the case we're dropping a multicol primary key.)
12559 * Then, verify whether any further NOT NULL or primary key
12560 * exists, and reset attnotnull if none.
12562 * However, if this is a generated identity column, abort the
12563 * whole thing with a specific error message, because the
12564 * constraint is required in that case.
12566 contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
12567 if (contup ||
12568 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
12569 pkcols))
12570 continue;
12573 * It's not valid to drop the not-null constraint for a GENERATED
12574 * AS IDENTITY column.
12576 if (attForm->attidentity)
12577 ereport(ERROR,
12578 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12579 errmsg("column \"%s\" of relation \"%s\" is an identity column",
12580 get_attname(RelationGetRelid(rel), attnum,
12581 false),
12582 RelationGetRelationName(rel)));
12585 * It's not valid to drop the not-null constraint for a column in
12586 * the replica identity index, either. (FULL is not affected.)
12588 if (bms_is_member(lfirst_int(lc) - FirstLowInvalidHeapAttributeNumber, ircols))
12589 ereport(ERROR,
12590 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12591 errmsg("column \"%s\" is in index used as replica identity",
12592 get_attname(RelationGetRelid(rel), lfirst_int(lc), false)));
12594 /* Reset attnotnull */
12595 if (attForm->attnotnull)
12597 attForm->attnotnull = false;
12598 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
12601 table_close(attrel, RowExclusiveLock);
12605 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
12606 * are dropped via the dependency mechanism, so we're done here.
12608 if (con->contype != CONSTRAINT_CHECK &&
12609 con->contype != CONSTRAINT_NOTNULL &&
12610 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12612 table_close(conrel, RowExclusiveLock);
12613 return conobj;
12617 * Propagate to children as appropriate. Unlike most other ALTER
12618 * routines, we have to do this one level of recursion at a time; we can't
12619 * use find_all_inheritors to do it in one pass.
12621 if (!is_no_inherit_constraint)
12622 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12623 else
12624 children = NIL;
12627 * For a partitioned table, if partitions exist and we are told not to
12628 * recurse, it's a user error. It doesn't make sense to have a constraint
12629 * be defined only on the parent, especially if it's a partitioned table.
12631 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
12632 children != NIL && !recurse)
12633 ereport(ERROR,
12634 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12635 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12636 errhint("Do not specify the ONLY keyword.")));
12638 /* For not-null constraints we recurse by column name */
12639 if (con->contype == CONSTRAINT_NOTNULL)
12640 colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
12641 linitial_int(unconstrained_cols) - 1)->attname);
12642 else
12643 colname = NULL; /* keep compiler quiet */
12645 foreach(child, children)
12647 Oid childrelid = lfirst_oid(child);
12648 Relation childrel;
12649 HeapTuple tuple;
12650 Form_pg_constraint childcon;
12652 if (list_member_oid(*readyRels, childrelid))
12653 continue; /* child already processed */
12655 /* find_inheritance_children already got lock */
12656 childrel = table_open(childrelid, NoLock);
12657 CheckTableNotInUse(childrel, "ALTER TABLE");
12660 * We search for not-null constraint by column number, and other
12661 * constraints by name.
12663 if (con->contype == CONSTRAINT_NOTNULL)
12665 tuple = findNotNullConstraint(childrelid, colname);
12666 if (!HeapTupleIsValid(tuple))
12667 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12668 colname, RelationGetRelid(childrel));
12670 else
12672 SysScanDesc scan;
12673 ScanKeyData skey[3];
12675 ScanKeyInit(&skey[0],
12676 Anum_pg_constraint_conrelid,
12677 BTEqualStrategyNumber, F_OIDEQ,
12678 ObjectIdGetDatum(childrelid));
12679 ScanKeyInit(&skey[1],
12680 Anum_pg_constraint_contypid,
12681 BTEqualStrategyNumber, F_OIDEQ,
12682 ObjectIdGetDatum(InvalidOid));
12683 ScanKeyInit(&skey[2],
12684 Anum_pg_constraint_conname,
12685 BTEqualStrategyNumber, F_NAMEEQ,
12686 CStringGetDatum(constrName));
12687 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12688 true, NULL, 3, skey);
12689 /* There can only be one, so no need to loop */
12690 tuple = systable_getnext(scan);
12691 if (!HeapTupleIsValid(tuple))
12692 ereport(ERROR,
12693 (errcode(ERRCODE_UNDEFINED_OBJECT),
12694 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12695 constrName,
12696 RelationGetRelationName(childrel))));
12697 tuple = heap_copytuple(tuple);
12698 systable_endscan(scan);
12701 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
12703 /* Right now only CHECK and not-null constraints can be inherited */
12704 if (childcon->contype != CONSTRAINT_CHECK &&
12705 childcon->contype != CONSTRAINT_NOTNULL)
12706 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
12708 if (childcon->coninhcount <= 0) /* shouldn't happen */
12709 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
12710 childrelid, NameStr(childcon->conname));
12712 if (recurse)
12715 * If the child constraint has other definition sources, just
12716 * decrement its inheritance count; if not, recurse to delete it.
12718 if (childcon->coninhcount == 1 && !childcon->conislocal)
12720 /* Time to delete this child constraint, too */
12721 dropconstraint_internal(childrel, tuple, behavior,
12722 recurse, true, missing_ok, readyRels,
12723 lockmode);
12725 else
12727 /* Child constraint must survive my deletion */
12728 childcon->coninhcount--;
12729 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
12731 /* Make update visible */
12732 CommandCounterIncrement();
12735 else
12738 * If we were told to drop ONLY in this table (no recursion) and
12739 * there are no further parents for this constraint, we need to
12740 * mark the inheritors' constraints as locally defined rather than
12741 * inherited.
12743 childcon->coninhcount--;
12744 if (childcon->coninhcount == 0)
12745 childcon->conislocal = true;
12747 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
12749 /* Make update visible */
12750 CommandCounterIncrement();
12753 heap_freetuple(tuple);
12755 table_close(childrel, NoLock);
12759 * In addition, when dropping a primary key from a legacy-inheritance
12760 * parent table, we must recurse to children to mark the corresponding NOT
12761 * NULL constraint as no longer inherited, or drop it if this its last
12762 * reference.
12764 if (con->contype == CONSTRAINT_PRIMARY &&
12765 rel->rd_rel->relkind == RELKIND_RELATION &&
12766 rel->rd_rel->relhassubclass)
12768 List *colnames = NIL;
12769 ListCell *lc;
12770 List *pkready = NIL;
12773 * Because primary keys are always marked as NO INHERIT, we don't have
12774 * a list of children yet, so obtain one now.
12776 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12779 * Find out the list of column names to process. Fortunately, we
12780 * already have the list of column numbers.
12782 foreach(lc, unconstrained_cols)
12784 colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
12785 lfirst_int(lc), false));
12788 foreach(child, children)
12790 Oid childrelid = lfirst_oid(child);
12791 Relation childrel;
12793 if (list_member_oid(pkready, childrelid))
12794 continue; /* child already processed */
12796 /* find_inheritance_children already got lock */
12797 childrel = table_open(childrelid, NoLock);
12798 CheckTableNotInUse(childrel, "ALTER TABLE");
12800 foreach(lc, colnames)
12802 HeapTuple contup;
12803 char *colName = lfirst(lc);
12805 contup = findNotNullConstraint(childrelid, colName);
12806 if (contup == NULL)
12807 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
12808 colName, RelationGetRelationName(childrel));
12810 dropconstraint_internal(childrel, contup,
12811 DROP_RESTRICT, true, true,
12812 false, &pkready,
12813 lockmode);
12814 pkready = NIL;
12817 table_close(childrel, NoLock);
12819 pkready = lappend_oid(pkready, childrelid);
12823 table_close(conrel, RowExclusiveLock);
12825 return conobj;
12829 * ALTER COLUMN TYPE
12831 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12832 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12833 * transformed (and must be, because we rely on some transformed fields).
12835 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12836 * table will be done "in parallel" during phase 3, so all the USING
12837 * expressions should be parsed assuming the original column types. Also,
12838 * this allows a USING expression to refer to a field that will be dropped.
12840 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12841 * the first two execution steps in phase 2; they must not see the effects
12842 * of any other subcommand types, since the USING expressions are parsed
12843 * against the unmodified table's state.
12845 static void
12846 ATPrepAlterColumnType(List **wqueue,
12847 AlteredTableInfo *tab, Relation rel,
12848 bool recurse, bool recursing,
12849 AlterTableCmd *cmd, LOCKMODE lockmode,
12850 AlterTableUtilityContext *context)
12852 char *colName = cmd->name;
12853 ColumnDef *def = (ColumnDef *) cmd->def;
12854 TypeName *typeName = def->typeName;
12855 Node *transform = def->cooked_default;
12856 HeapTuple tuple;
12857 Form_pg_attribute attTup;
12858 AttrNumber attnum;
12859 Oid targettype;
12860 int32 targettypmod;
12861 Oid targetcollid;
12862 NewColumnValue *newval;
12863 ParseState *pstate = make_parsestate(NULL);
12864 AclResult aclresult;
12865 bool is_expr;
12867 if (rel->rd_rel->reloftype && !recursing)
12868 ereport(ERROR,
12869 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12870 errmsg("cannot alter column type of typed table")));
12872 /* lookup the attribute so we can check inheritance status */
12873 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12874 if (!HeapTupleIsValid(tuple))
12875 ereport(ERROR,
12876 (errcode(ERRCODE_UNDEFINED_COLUMN),
12877 errmsg("column \"%s\" of relation \"%s\" does not exist",
12878 colName, RelationGetRelationName(rel))));
12879 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12880 attnum = attTup->attnum;
12882 /* Can't alter a system attribute */
12883 if (attnum <= 0)
12884 ereport(ERROR,
12885 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12886 errmsg("cannot alter system column \"%s\"",
12887 colName)));
12890 * Don't alter inherited columns. At outer level, there had better not be
12891 * any inherited definition; when recursing, we assume this was checked at
12892 * the parent level (see below).
12894 if (attTup->attinhcount > 0 && !recursing)
12895 ereport(ERROR,
12896 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12897 errmsg("cannot alter inherited column \"%s\"",
12898 colName)));
12900 /* Don't alter columns used in the partition key */
12901 if (has_partition_attrs(rel,
12902 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
12903 &is_expr))
12904 ereport(ERROR,
12905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12906 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12907 colName, RelationGetRelationName(rel))));
12909 /* Look up the target type */
12910 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
12912 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
12913 if (aclresult != ACLCHECK_OK)
12914 aclcheck_error_type(aclresult, targettype);
12916 /* And the collation */
12917 targetcollid = GetColumnDefCollation(NULL, def, targettype);
12919 /* make sure datatype is legal for a column */
12920 CheckAttributeType(colName, targettype, targetcollid,
12921 list_make1_oid(rel->rd_rel->reltype),
12924 if (tab->relkind == RELKIND_RELATION ||
12925 tab->relkind == RELKIND_PARTITIONED_TABLE)
12928 * Set up an expression to transform the old data value to the new
12929 * type. If a USING option was given, use the expression as
12930 * transformed by transformAlterTableStmt, else just take the old
12931 * value and try to coerce it. We do this first so that type
12932 * incompatibility can be detected before we waste effort, and because
12933 * we need the expression to be parsed against the original table row
12934 * type.
12936 if (!transform)
12938 transform = (Node *) makeVar(1, attnum,
12939 attTup->atttypid, attTup->atttypmod,
12940 attTup->attcollation,
12944 transform = coerce_to_target_type(pstate,
12945 transform, exprType(transform),
12946 targettype, targettypmod,
12947 COERCION_ASSIGNMENT,
12948 COERCE_IMPLICIT_CAST,
12949 -1);
12950 if (transform == NULL)
12952 /* error text depends on whether USING was specified or not */
12953 if (def->cooked_default != NULL)
12954 ereport(ERROR,
12955 (errcode(ERRCODE_DATATYPE_MISMATCH),
12956 errmsg("result of USING clause for column \"%s\""
12957 " cannot be cast automatically to type %s",
12958 colName, format_type_be(targettype)),
12959 errhint("You might need to add an explicit cast.")));
12960 else
12961 ereport(ERROR,
12962 (errcode(ERRCODE_DATATYPE_MISMATCH),
12963 errmsg("column \"%s\" cannot be cast automatically to type %s",
12964 colName, format_type_be(targettype)),
12965 /* translator: USING is SQL, don't translate it */
12966 errhint("You might need to specify \"USING %s::%s\".",
12967 quote_identifier(colName),
12968 format_type_with_typemod(targettype,
12969 targettypmod))));
12972 /* Fix collations after all else */
12973 assign_expr_collations(pstate, transform);
12975 /* Plan the expr now so we can accurately assess the need to rewrite. */
12976 transform = (Node *) expression_planner((Expr *) transform);
12979 * Add a work queue item to make ATRewriteTable update the column
12980 * contents.
12982 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
12983 newval->attnum = attnum;
12984 newval->expr = (Expr *) transform;
12985 newval->is_generated = false;
12987 tab->newvals = lappend(tab->newvals, newval);
12988 if (ATColumnChangeRequiresRewrite(transform, attnum))
12989 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
12991 else if (transform)
12992 ereport(ERROR,
12993 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12994 errmsg("\"%s\" is not a table",
12995 RelationGetRelationName(rel))));
12997 if (!RELKIND_HAS_STORAGE(tab->relkind))
13000 * For relations without storage, do this check now. Regular tables
13001 * will check it later when the table is being rewritten.
13003 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13006 ReleaseSysCache(tuple);
13009 * Recurse manually by queueing a new command for each child, if
13010 * necessary. We cannot apply ATSimpleRecursion here because we need to
13011 * remap attribute numbers in the USING expression, if any.
13013 * If we are told not to recurse, there had better not be any child
13014 * tables; else the alter would put them out of step.
13016 if (recurse)
13018 Oid relid = RelationGetRelid(rel);
13019 List *child_oids,
13020 *child_numparents;
13021 ListCell *lo,
13022 *li;
13024 child_oids = find_all_inheritors(relid, lockmode,
13025 &child_numparents);
13028 * find_all_inheritors does the recursive search of the inheritance
13029 * hierarchy, so all we have to do is process all of the relids in the
13030 * list that it returns.
13032 forboth(lo, child_oids, li, child_numparents)
13034 Oid childrelid = lfirst_oid(lo);
13035 int numparents = lfirst_int(li);
13036 Relation childrel;
13037 HeapTuple childtuple;
13038 Form_pg_attribute childattTup;
13040 if (childrelid == relid)
13041 continue;
13043 /* find_all_inheritors already got lock */
13044 childrel = relation_open(childrelid, NoLock);
13045 CheckTableNotInUse(childrel, "ALTER TABLE");
13048 * Verify that the child doesn't have any inherited definitions of
13049 * this column that came from outside this inheritance hierarchy.
13050 * (renameatt makes a similar test, though in a different way
13051 * because of its different recursion mechanism.)
13053 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13054 colName);
13055 if (!HeapTupleIsValid(childtuple))
13056 ereport(ERROR,
13057 (errcode(ERRCODE_UNDEFINED_COLUMN),
13058 errmsg("column \"%s\" of relation \"%s\" does not exist",
13059 colName, RelationGetRelationName(childrel))));
13060 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13062 if (childattTup->attinhcount > numparents)
13063 ereport(ERROR,
13064 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13065 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13066 colName, RelationGetRelationName(childrel))));
13068 ReleaseSysCache(childtuple);
13071 * Remap the attribute numbers. If no USING expression was
13072 * specified, there is no need for this step.
13074 if (def->cooked_default)
13076 AttrMap *attmap;
13077 bool found_whole_row;
13079 /* create a copy to scribble on */
13080 cmd = copyObject(cmd);
13082 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13083 RelationGetDescr(rel),
13084 false);
13085 ((ColumnDef *) cmd->def)->cooked_default =
13086 map_variable_attnos(def->cooked_default,
13087 1, 0,
13088 attmap,
13089 InvalidOid, &found_whole_row);
13090 if (found_whole_row)
13091 ereport(ERROR,
13092 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13093 errmsg("cannot convert whole-row table reference"),
13094 errdetail("USING expression contains a whole-row table reference.")));
13095 pfree(attmap);
13097 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13098 relation_close(childrel, NoLock);
13101 else if (!recursing &&
13102 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13103 ereport(ERROR,
13104 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13105 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13106 colName)));
13108 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13109 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13113 * When the data type of a column is changed, a rewrite might not be required
13114 * if the new type is sufficiently identical to the old one, and the USING
13115 * clause isn't trying to insert some other value. It's safe to skip the
13116 * rewrite in these cases:
13118 * - the old type is binary coercible to the new type
13119 * - the new type is an unconstrained domain over the old type
13120 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13122 * In the case of a constrained domain, we could get by with scanning the
13123 * table and checking the constraint rather than actually rewriting it, but we
13124 * don't currently try to do that.
13126 static bool
13127 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13129 Assert(expr != NULL);
13131 for (;;)
13133 /* only one varno, so no need to check that */
13134 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13135 return false;
13136 else if (IsA(expr, RelabelType))
13137 expr = (Node *) ((RelabelType *) expr)->arg;
13138 else if (IsA(expr, CoerceToDomain))
13140 CoerceToDomain *d = (CoerceToDomain *) expr;
13142 if (DomainHasConstraints(d->resulttype))
13143 return true;
13144 expr = (Node *) d->arg;
13146 else if (IsA(expr, FuncExpr))
13148 FuncExpr *f = (FuncExpr *) expr;
13150 switch (f->funcid)
13152 case F_TIMESTAMPTZ_TIMESTAMP:
13153 case F_TIMESTAMP_TIMESTAMPTZ:
13154 if (TimestampTimestampTzRequiresRewrite())
13155 return true;
13156 else
13157 expr = linitial(f->args);
13158 break;
13159 default:
13160 return true;
13163 else
13164 return true;
13169 * ALTER COLUMN .. SET DATA TYPE
13171 * Return the address of the modified column.
13173 static ObjectAddress
13174 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13175 AlterTableCmd *cmd, LOCKMODE lockmode)
13177 char *colName = cmd->name;
13178 ColumnDef *def = (ColumnDef *) cmd->def;
13179 TypeName *typeName = def->typeName;
13180 HeapTuple heapTup;
13181 Form_pg_attribute attTup,
13182 attOldTup;
13183 AttrNumber attnum;
13184 HeapTuple typeTuple;
13185 Form_pg_type tform;
13186 Oid targettype;
13187 int32 targettypmod;
13188 Oid targetcollid;
13189 Node *defaultexpr;
13190 Relation attrelation;
13191 Relation depRel;
13192 ScanKeyData key[3];
13193 SysScanDesc scan;
13194 HeapTuple depTup;
13195 ObjectAddress address;
13198 * Clear all the missing values if we're rewriting the table, since this
13199 * renders them pointless.
13201 if (tab->rewrite)
13203 Relation newrel;
13205 newrel = table_open(RelationGetRelid(rel), NoLock);
13206 RelationClearMissing(newrel);
13207 relation_close(newrel, NoLock);
13208 /* make sure we don't conflict with later attribute modifications */
13209 CommandCounterIncrement();
13212 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13214 /* Look up the target column */
13215 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13216 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13217 ereport(ERROR,
13218 (errcode(ERRCODE_UNDEFINED_COLUMN),
13219 errmsg("column \"%s\" of relation \"%s\" does not exist",
13220 colName, RelationGetRelationName(rel))));
13221 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13222 attnum = attTup->attnum;
13223 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13225 /* Check for multiple ALTER TYPE on same column --- can't cope */
13226 if (attTup->atttypid != attOldTup->atttypid ||
13227 attTup->atttypmod != attOldTup->atttypmod)
13228 ereport(ERROR,
13229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13230 errmsg("cannot alter type of column \"%s\" twice",
13231 colName)));
13233 /* Look up the target type (should not fail, since prep found it) */
13234 typeTuple = typenameType(NULL, typeName, &targettypmod);
13235 tform = (Form_pg_type) GETSTRUCT(typeTuple);
13236 targettype = tform->oid;
13237 /* And the collation */
13238 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13241 * If there is a default expression for the column, get it and ensure we
13242 * can coerce it to the new datatype. (We must do this before changing
13243 * the column type, because build_column_default itself will try to
13244 * coerce, and will not issue the error message we want if it fails.)
13246 * We remove any implicit coercion steps at the top level of the old
13247 * default expression; this has been agreed to satisfy the principle of
13248 * least surprise. (The conversion to the new column type should act like
13249 * it started from what the user sees as the stored expression, and the
13250 * implicit coercions aren't going to be shown.)
13252 if (attTup->atthasdef)
13254 defaultexpr = build_column_default(rel, attnum);
13255 Assert(defaultexpr);
13256 defaultexpr = strip_implicit_coercions(defaultexpr);
13257 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13258 defaultexpr, exprType(defaultexpr),
13259 targettype, targettypmod,
13260 COERCION_ASSIGNMENT,
13261 COERCE_IMPLICIT_CAST,
13262 -1);
13263 if (defaultexpr == NULL)
13265 if (attTup->attgenerated)
13266 ereport(ERROR,
13267 (errcode(ERRCODE_DATATYPE_MISMATCH),
13268 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13269 colName, format_type_be(targettype))));
13270 else
13271 ereport(ERROR,
13272 (errcode(ERRCODE_DATATYPE_MISMATCH),
13273 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13274 colName, format_type_be(targettype))));
13277 else
13278 defaultexpr = NULL;
13281 * Find everything that depends on the column (constraints, indexes, etc),
13282 * and record enough information to let us recreate the objects.
13284 * The actual recreation does not happen here, but only after we have
13285 * performed all the individual ALTER TYPE operations. We have to save
13286 * the info before executing ALTER TYPE, though, else the deparser will
13287 * get confused.
13289 depRel = table_open(DependRelationId, RowExclusiveLock);
13291 ScanKeyInit(&key[0],
13292 Anum_pg_depend_refclassid,
13293 BTEqualStrategyNumber, F_OIDEQ,
13294 ObjectIdGetDatum(RelationRelationId));
13295 ScanKeyInit(&key[1],
13296 Anum_pg_depend_refobjid,
13297 BTEqualStrategyNumber, F_OIDEQ,
13298 ObjectIdGetDatum(RelationGetRelid(rel)));
13299 ScanKeyInit(&key[2],
13300 Anum_pg_depend_refobjsubid,
13301 BTEqualStrategyNumber, F_INT4EQ,
13302 Int32GetDatum((int32) attnum));
13304 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13305 NULL, 3, key);
13307 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13309 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13310 ObjectAddress foundObject;
13312 foundObject.classId = foundDep->classid;
13313 foundObject.objectId = foundDep->objid;
13314 foundObject.objectSubId = foundDep->objsubid;
13316 switch (getObjectClass(&foundObject))
13318 case OCLASS_CLASS:
13320 char relKind = get_rel_relkind(foundObject.objectId);
13322 if (relKind == RELKIND_INDEX ||
13323 relKind == RELKIND_PARTITIONED_INDEX)
13325 Assert(foundObject.objectSubId == 0);
13326 RememberIndexForRebuilding(foundObject.objectId, tab);
13328 else if (relKind == RELKIND_SEQUENCE)
13331 * This must be a SERIAL column's sequence. We need
13332 * not do anything to it.
13334 Assert(foundObject.objectSubId == 0);
13336 else
13338 /* Not expecting any other direct dependencies... */
13339 elog(ERROR, "unexpected object depending on column: %s",
13340 getObjectDescription(&foundObject, false));
13342 break;
13345 case OCLASS_CONSTRAINT:
13346 Assert(foundObject.objectSubId == 0);
13347 RememberConstraintForRebuilding(foundObject.objectId, tab);
13348 break;
13350 case OCLASS_REWRITE:
13351 /* XXX someday see if we can cope with revising views */
13352 ereport(ERROR,
13353 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13354 errmsg("cannot alter type of a column used by a view or rule"),
13355 errdetail("%s depends on column \"%s\"",
13356 getObjectDescription(&foundObject, false),
13357 colName)));
13358 break;
13360 case OCLASS_TRIGGER:
13363 * A trigger can depend on a column because the column is
13364 * specified as an update target, or because the column is
13365 * used in the trigger's WHEN condition. The first case would
13366 * not require any extra work, but the second case would
13367 * require updating the WHEN expression, which will take a
13368 * significant amount of new code. Since we can't easily tell
13369 * which case applies, we punt for both. FIXME someday.
13371 ereport(ERROR,
13372 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13373 errmsg("cannot alter type of a column used in a trigger definition"),
13374 errdetail("%s depends on column \"%s\"",
13375 getObjectDescription(&foundObject, false),
13376 colName)));
13377 break;
13379 case OCLASS_POLICY:
13382 * A policy can depend on a column because the column is
13383 * specified in the policy's USING or WITH CHECK qual
13384 * expressions. It might be possible to rewrite and recheck
13385 * the policy expression, but punt for now. It's certainly
13386 * easy enough to remove and recreate the policy; still, FIXME
13387 * someday.
13389 ereport(ERROR,
13390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13391 errmsg("cannot alter type of a column used in a policy definition"),
13392 errdetail("%s depends on column \"%s\"",
13393 getObjectDescription(&foundObject, false),
13394 colName)));
13395 break;
13397 case OCLASS_DEFAULT:
13399 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
13401 if (col.objectId == RelationGetRelid(rel) &&
13402 col.objectSubId == attnum)
13405 * Ignore the column's own default expression, which
13406 * we will deal with below.
13408 Assert(defaultexpr);
13410 else
13413 * This must be a reference from the expression of a
13414 * generated column elsewhere in the same table.
13415 * Changing the type of a column that is used by a
13416 * generated column is not allowed by SQL standard, so
13417 * just punt for now. It might be doable with some
13418 * thinking and effort.
13420 ereport(ERROR,
13421 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13422 errmsg("cannot alter type of a column used by a generated column"),
13423 errdetail("Column \"%s\" is used by generated column \"%s\".",
13424 colName,
13425 get_attname(col.objectId,
13426 col.objectSubId,
13427 false))));
13429 break;
13432 case OCLASS_STATISTIC_EXT:
13435 * Give the extended-stats machinery a chance to fix anything
13436 * that this column type change would break.
13438 RememberStatisticsForRebuilding(foundObject.objectId, tab);
13439 break;
13441 case OCLASS_PROC:
13442 case OCLASS_TYPE:
13443 case OCLASS_CAST:
13444 case OCLASS_COLLATION:
13445 case OCLASS_CONVERSION:
13446 case OCLASS_LANGUAGE:
13447 case OCLASS_LARGEOBJECT:
13448 case OCLASS_OPERATOR:
13449 case OCLASS_OPCLASS:
13450 case OCLASS_OPFAMILY:
13451 case OCLASS_AM:
13452 case OCLASS_AMOP:
13453 case OCLASS_AMPROC:
13454 case OCLASS_SCHEMA:
13455 case OCLASS_TSPARSER:
13456 case OCLASS_TSDICT:
13457 case OCLASS_TSTEMPLATE:
13458 case OCLASS_TSCONFIG:
13459 case OCLASS_ROLE:
13460 case OCLASS_ROLE_MEMBERSHIP:
13461 case OCLASS_DATABASE:
13462 case OCLASS_TBLSPACE:
13463 case OCLASS_FDW:
13464 case OCLASS_FOREIGN_SERVER:
13465 case OCLASS_USER_MAPPING:
13466 case OCLASS_DEFACL:
13467 case OCLASS_EXTENSION:
13468 case OCLASS_EVENT_TRIGGER:
13469 case OCLASS_PARAMETER_ACL:
13470 case OCLASS_PUBLICATION:
13471 case OCLASS_PUBLICATION_NAMESPACE:
13472 case OCLASS_PUBLICATION_REL:
13473 case OCLASS_SUBSCRIPTION:
13474 case OCLASS_TRANSFORM:
13477 * We don't expect any of these sorts of objects to depend on
13478 * a column.
13480 elog(ERROR, "unexpected object depending on column: %s",
13481 getObjectDescription(&foundObject, false));
13482 break;
13485 * There's intentionally no default: case here; we want the
13486 * compiler to warn if a new OCLASS hasn't been handled above.
13491 systable_endscan(scan);
13494 * Now scan for dependencies of this column on other things. The only
13495 * things we should find are the dependency on the column datatype and
13496 * possibly a collation dependency. Those can be removed.
13498 ScanKeyInit(&key[0],
13499 Anum_pg_depend_classid,
13500 BTEqualStrategyNumber, F_OIDEQ,
13501 ObjectIdGetDatum(RelationRelationId));
13502 ScanKeyInit(&key[1],
13503 Anum_pg_depend_objid,
13504 BTEqualStrategyNumber, F_OIDEQ,
13505 ObjectIdGetDatum(RelationGetRelid(rel)));
13506 ScanKeyInit(&key[2],
13507 Anum_pg_depend_objsubid,
13508 BTEqualStrategyNumber, F_INT4EQ,
13509 Int32GetDatum((int32) attnum));
13511 scan = systable_beginscan(depRel, DependDependerIndexId, true,
13512 NULL, 3, key);
13514 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13516 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13517 ObjectAddress foundObject;
13519 foundObject.classId = foundDep->refclassid;
13520 foundObject.objectId = foundDep->refobjid;
13521 foundObject.objectSubId = foundDep->refobjsubid;
13523 if (foundDep->deptype != DEPENDENCY_NORMAL)
13524 elog(ERROR, "found unexpected dependency type '%c'",
13525 foundDep->deptype);
13526 if (!(foundDep->refclassid == TypeRelationId &&
13527 foundDep->refobjid == attTup->atttypid) &&
13528 !(foundDep->refclassid == CollationRelationId &&
13529 foundDep->refobjid == attTup->attcollation))
13530 elog(ERROR, "found unexpected dependency for column: %s",
13531 getObjectDescription(&foundObject, false));
13533 CatalogTupleDelete(depRel, &depTup->t_self);
13536 systable_endscan(scan);
13538 table_close(depRel, RowExclusiveLock);
13541 * Here we go --- change the recorded column type and collation. (Note
13542 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13543 * fix up the missing value if any.
13545 if (attTup->atthasmissing)
13547 Datum missingval;
13548 bool missingNull;
13550 /* if rewrite is true the missing value should already be cleared */
13551 Assert(tab->rewrite == 0);
13553 /* Get the missing value datum */
13554 missingval = heap_getattr(heapTup,
13555 Anum_pg_attribute_attmissingval,
13556 attrelation->rd_att,
13557 &missingNull);
13559 /* if it's a null array there is nothing to do */
13561 if (!missingNull)
13564 * Get the datum out of the array and repack it in a new array
13565 * built with the new type data. We assume that since the table
13566 * doesn't need rewriting, the actual Datum doesn't need to be
13567 * changed, only the array metadata.
13570 int one = 1;
13571 bool isNull;
13572 Datum valuesAtt[Natts_pg_attribute] = {0};
13573 bool nullsAtt[Natts_pg_attribute] = {0};
13574 bool replacesAtt[Natts_pg_attribute] = {0};
13575 HeapTuple newTup;
13577 missingval = array_get_element(missingval,
13579 &one,
13581 attTup->attlen,
13582 attTup->attbyval,
13583 attTup->attalign,
13584 &isNull);
13585 missingval = PointerGetDatum(construct_array(&missingval,
13587 targettype,
13588 tform->typlen,
13589 tform->typbyval,
13590 tform->typalign));
13592 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13593 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13594 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13596 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13597 valuesAtt, nullsAtt, replacesAtt);
13598 heap_freetuple(heapTup);
13599 heapTup = newTup;
13600 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13604 attTup->atttypid = targettype;
13605 attTup->atttypmod = targettypmod;
13606 attTup->attcollation = targetcollid;
13607 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13608 ereport(ERROR,
13609 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13610 errmsg("too many array dimensions"));
13611 attTup->attndims = list_length(typeName->arrayBounds);
13612 attTup->attlen = tform->typlen;
13613 attTup->attbyval = tform->typbyval;
13614 attTup->attalign = tform->typalign;
13615 attTup->attstorage = tform->typstorage;
13616 attTup->attcompression = InvalidCompressionMethod;
13618 ReleaseSysCache(typeTuple);
13620 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13622 table_close(attrelation, RowExclusiveLock);
13624 /* Install dependencies on new datatype and collation */
13625 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13626 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13629 * Drop any pg_statistic entry for the column, since it's now wrong type
13631 RemoveStatistics(RelationGetRelid(rel), attnum);
13633 InvokeObjectPostAlterHook(RelationRelationId,
13634 RelationGetRelid(rel), attnum);
13637 * Update the default, if present, by brute force --- remove and re-add
13638 * the default. Probably unsafe to take shortcuts, since the new version
13639 * may well have additional dependencies. (It's okay to do this now,
13640 * rather than after other ALTER TYPE commands, since the default won't
13641 * depend on other column types.)
13643 if (defaultexpr)
13646 * If it's a GENERATED default, drop its dependency records, in
13647 * particular its INTERNAL dependency on the column, which would
13648 * otherwise cause dependency.c to refuse to perform the deletion.
13650 if (attTup->attgenerated)
13652 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13654 if (!OidIsValid(attrdefoid))
13655 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13656 RelationGetRelid(rel), attnum);
13657 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13661 * Make updates-so-far visible, particularly the new pg_attribute row
13662 * which will be updated again.
13664 CommandCounterIncrement();
13667 * We use RESTRICT here for safety, but at present we do not expect
13668 * anything to depend on the default.
13670 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13671 true);
13673 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13676 ObjectAddressSubSet(address, RelationRelationId,
13677 RelationGetRelid(rel), attnum);
13679 /* Cleanup */
13680 heap_freetuple(heapTup);
13682 return address;
13686 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13687 * needs to be reset.
13689 static void
13690 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
13692 if (!get_index_isreplident(indoid))
13693 return;
13695 if (tab->replicaIdentityIndex)
13696 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
13698 tab->replicaIdentityIndex = get_rel_name(indoid);
13702 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13704 static void
13705 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
13707 if (!get_index_isclustered(indoid))
13708 return;
13710 if (tab->clusterOnIndex)
13711 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13713 tab->clusterOnIndex = get_rel_name(indoid);
13717 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13718 * to be rebuilt (which we might already know).
13720 static void
13721 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
13724 * This de-duplication check is critical for two independent reasons: we
13725 * mustn't try to recreate the same constraint twice, and if a constraint
13726 * depends on more than one column whose type is to be altered, we must
13727 * capture its definition string before applying any of the column type
13728 * changes. ruleutils.c will get confused if we ask again later.
13730 if (!list_member_oid(tab->changedConstraintOids, conoid))
13732 /* OK, capture the constraint's existing definition string */
13733 char *defstring = pg_get_constraintdef_command(conoid);
13734 Oid indoid;
13736 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
13737 conoid);
13738 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
13739 defstring);
13742 * For the index of a constraint, if any, remember if it is used for
13743 * the table's replica identity or if it is a clustered index, so that
13744 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13745 * those properties.
13747 indoid = get_constraint_index(conoid);
13748 if (OidIsValid(indoid))
13750 RememberReplicaIdentityForRebuilding(indoid, tab);
13751 RememberClusterOnForRebuilding(indoid, tab);
13757 * Subroutine for ATExecAlterColumnType: remember that an index needs
13758 * to be rebuilt (which we might already know).
13760 static void
13761 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
13764 * This de-duplication check is critical for two independent reasons: we
13765 * mustn't try to recreate the same index twice, and if an index depends
13766 * on more than one column whose type is to be altered, we must capture
13767 * its definition string before applying any of the column type changes.
13768 * ruleutils.c will get confused if we ask again later.
13770 if (!list_member_oid(tab->changedIndexOids, indoid))
13773 * Before adding it as an index-to-rebuild, we'd better see if it
13774 * belongs to a constraint, and if so rebuild the constraint instead.
13775 * Typically this check fails, because constraint indexes normally
13776 * have only dependencies on their constraint. But it's possible for
13777 * such an index to also have direct dependencies on table columns,
13778 * for example with a partial exclusion constraint.
13780 Oid conoid = get_index_constraint(indoid);
13782 if (OidIsValid(conoid))
13784 RememberConstraintForRebuilding(conoid, tab);
13786 else
13788 /* OK, capture the index's existing definition string */
13789 char *defstring = pg_get_indexdef_string(indoid);
13791 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
13792 indoid);
13793 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
13794 defstring);
13797 * Remember if this index is used for the table's replica identity
13798 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13799 * can queue up commands necessary to restore those properties.
13801 RememberReplicaIdentityForRebuilding(indoid, tab);
13802 RememberClusterOnForRebuilding(indoid, tab);
13808 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13809 * needs to be rebuilt (which we might already know).
13811 static void
13812 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13815 * This de-duplication check is critical for two independent reasons: we
13816 * mustn't try to recreate the same statistics object twice, and if the
13817 * statistics object depends on more than one column whose type is to be
13818 * altered, we must capture its definition string before applying any of
13819 * the type changes. ruleutils.c will get confused if we ask again later.
13821 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
13823 /* OK, capture the statistics object's existing definition string */
13824 char *defstring = pg_get_statisticsobjdef_string(stxoid);
13826 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
13827 stxoid);
13828 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
13829 defstring);
13834 * Cleanup after we've finished all the ALTER TYPE operations for a
13835 * particular relation. We have to drop and recreate all the indexes
13836 * and constraints that depend on the altered columns. We do the
13837 * actual dropping here, but re-creation is managed by adding work
13838 * queue entries to do those steps later.
13840 static void
13841 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
13843 ObjectAddress obj;
13844 ObjectAddresses *objects;
13845 ListCell *def_item;
13846 ListCell *oid_item;
13849 * Collect all the constraints and indexes to drop so we can process them
13850 * in a single call. That way we don't have to worry about dependencies
13851 * among them.
13853 objects = new_object_addresses();
13856 * Re-parse the index and constraint definitions, and attach them to the
13857 * appropriate work queue entries. We do this before dropping because in
13858 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13859 * lock on the table the constraint is attached to, and we need to get
13860 * that before reparsing/dropping.
13862 * We can't rely on the output of deparsing to tell us which relation to
13863 * operate on, because concurrent activity might have made the name
13864 * resolve differently. Instead, we've got to use the OID of the
13865 * constraint or index we're processing to figure out which relation to
13866 * operate on.
13868 forboth(oid_item, tab->changedConstraintOids,
13869 def_item, tab->changedConstraintDefs)
13871 Oid oldId = lfirst_oid(oid_item);
13872 HeapTuple tup;
13873 Form_pg_constraint con;
13874 Oid relid;
13875 Oid confrelid;
13876 char contype;
13877 bool conislocal;
13879 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13880 if (!HeapTupleIsValid(tup)) /* should not happen */
13881 elog(ERROR, "cache lookup failed for constraint %u", oldId);
13882 con = (Form_pg_constraint) GETSTRUCT(tup);
13883 if (OidIsValid(con->conrelid))
13884 relid = con->conrelid;
13885 else
13887 /* must be a domain constraint */
13888 relid = get_typ_typrelid(getBaseType(con->contypid));
13889 if (!OidIsValid(relid))
13890 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13892 confrelid = con->confrelid;
13893 contype = con->contype;
13894 conislocal = con->conislocal;
13895 ReleaseSysCache(tup);
13897 ObjectAddressSet(obj, ConstraintRelationId, oldId);
13898 add_exact_object_address(&obj, objects);
13901 * If the constraint is inherited (only), we don't want to inject a
13902 * new definition here; it'll get recreated when
13903 * ATAddCheckNNConstraint recurses from adding the parent table's
13904 * constraint. But we had to carry the info this far so that we can
13905 * drop the constraint below.
13907 if (!conislocal)
13908 continue;
13911 * When rebuilding an FK constraint that references the table we're
13912 * modifying, we might not yet have any lock on the FK's table, so get
13913 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13914 * step, so there's no value in asking for anything weaker.
13916 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
13917 LockRelationOid(relid, AccessExclusiveLock);
13919 ATPostAlterTypeParse(oldId, relid, confrelid,
13920 (char *) lfirst(def_item),
13921 wqueue, lockmode, tab->rewrite);
13923 forboth(oid_item, tab->changedIndexOids,
13924 def_item, tab->changedIndexDefs)
13926 Oid oldId = lfirst_oid(oid_item);
13927 Oid relid;
13929 relid = IndexGetRelation(oldId, false);
13930 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13931 (char *) lfirst(def_item),
13932 wqueue, lockmode, tab->rewrite);
13934 ObjectAddressSet(obj, RelationRelationId, oldId);
13935 add_exact_object_address(&obj, objects);
13938 /* add dependencies for new statistics */
13939 forboth(oid_item, tab->changedStatisticsOids,
13940 def_item, tab->changedStatisticsDefs)
13942 Oid oldId = lfirst_oid(oid_item);
13943 Oid relid;
13945 relid = StatisticsGetRelation(oldId, false);
13946 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13947 (char *) lfirst(def_item),
13948 wqueue, lockmode, tab->rewrite);
13950 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
13951 add_exact_object_address(&obj, objects);
13955 * Queue up command to restore replica identity index marking
13957 if (tab->replicaIdentityIndex)
13959 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13960 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
13962 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13963 subcmd->name = tab->replicaIdentityIndex;
13964 cmd->subtype = AT_ReplicaIdentity;
13965 cmd->def = (Node *) subcmd;
13967 /* do it after indexes and constraints */
13968 tab->subcmds[AT_PASS_OLD_CONSTR] =
13969 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13973 * Queue up command to restore marking of index used for cluster.
13975 if (tab->clusterOnIndex)
13977 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13979 cmd->subtype = AT_ClusterOn;
13980 cmd->name = tab->clusterOnIndex;
13982 /* do it after indexes and constraints */
13983 tab->subcmds[AT_PASS_OLD_CONSTR] =
13984 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13988 * It should be okay to use DROP_RESTRICT here, since nothing else should
13989 * be depending on these objects.
13991 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
13993 free_object_addresses(objects);
13996 * The objects will get recreated during subsequent passes over the work
13997 * queue.
14002 * Parse the previously-saved definition string for a constraint, index or
14003 * statistics object against the newly-established column data type(s), and
14004 * queue up the resulting command parsetrees for execution.
14006 * This might fail if, for example, you have a WHERE clause that uses an
14007 * operator that's not available for the new column type.
14009 static void
14010 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14011 List **wqueue, LOCKMODE lockmode, bool rewrite)
14013 List *raw_parsetree_list;
14014 List *querytree_list;
14015 ListCell *list_item;
14016 Relation rel;
14019 * We expect that we will get only ALTER TABLE and CREATE INDEX
14020 * statements. Hence, there is no need to pass them through
14021 * parse_analyze_*() or the rewriter, but instead we need to pass them
14022 * through parse_utilcmd.c to make them ready for execution.
14024 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14025 querytree_list = NIL;
14026 foreach(list_item, raw_parsetree_list)
14028 RawStmt *rs = lfirst_node(RawStmt, list_item);
14029 Node *stmt = rs->stmt;
14031 if (IsA(stmt, IndexStmt))
14032 querytree_list = lappend(querytree_list,
14033 transformIndexStmt(oldRelId,
14034 (IndexStmt *) stmt,
14035 cmd));
14036 else if (IsA(stmt, AlterTableStmt))
14038 List *beforeStmts;
14039 List *afterStmts;
14041 stmt = (Node *) transformAlterTableStmt(oldRelId,
14042 (AlterTableStmt *) stmt,
14043 cmd,
14044 &beforeStmts,
14045 &afterStmts);
14046 querytree_list = list_concat(querytree_list, beforeStmts);
14047 querytree_list = lappend(querytree_list, stmt);
14048 querytree_list = list_concat(querytree_list, afterStmts);
14050 else if (IsA(stmt, CreateStatsStmt))
14051 querytree_list = lappend(querytree_list,
14052 transformStatsStmt(oldRelId,
14053 (CreateStatsStmt *) stmt,
14054 cmd));
14055 else
14056 querytree_list = lappend(querytree_list, stmt);
14059 /* Caller should already have acquired whatever lock we need. */
14060 rel = relation_open(oldRelId, NoLock);
14063 * Attach each generated command to the proper place in the work queue.
14064 * Note this could result in creation of entirely new work-queue entries.
14066 * Also note that we have to tweak the command subtypes, because it turns
14067 * out that re-creation of indexes and constraints has to act a bit
14068 * differently from initial creation.
14070 foreach(list_item, querytree_list)
14072 Node *stm = (Node *) lfirst(list_item);
14073 AlteredTableInfo *tab;
14075 tab = ATGetQueueEntry(wqueue, rel);
14077 if (IsA(stm, IndexStmt))
14079 IndexStmt *stmt = (IndexStmt *) stm;
14080 AlterTableCmd *newcmd;
14082 if (!rewrite)
14083 TryReuseIndex(oldId, stmt);
14084 stmt->reset_default_tblspc = true;
14085 /* keep the index's comment */
14086 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14088 newcmd = makeNode(AlterTableCmd);
14089 newcmd->subtype = AT_ReAddIndex;
14090 newcmd->def = (Node *) stmt;
14091 tab->subcmds[AT_PASS_OLD_INDEX] =
14092 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14094 else if (IsA(stm, AlterTableStmt))
14096 AlterTableStmt *stmt = (AlterTableStmt *) stm;
14097 ListCell *lcmd;
14099 foreach(lcmd, stmt->cmds)
14101 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14103 if (cmd->subtype == AT_AddIndex)
14105 IndexStmt *indstmt;
14106 Oid indoid;
14108 indstmt = castNode(IndexStmt, cmd->def);
14109 indoid = get_constraint_index(oldId);
14111 if (!rewrite)
14112 TryReuseIndex(indoid, indstmt);
14113 /* keep any comment on the index */
14114 indstmt->idxcomment = GetComment(indoid,
14115 RelationRelationId, 0);
14116 indstmt->reset_default_tblspc = true;
14118 cmd->subtype = AT_ReAddIndex;
14119 tab->subcmds[AT_PASS_OLD_INDEX] =
14120 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14122 /* recreate any comment on the constraint */
14123 RebuildConstraintComment(tab,
14124 AT_PASS_OLD_INDEX,
14125 oldId,
14126 rel,
14127 NIL,
14128 indstmt->idxname);
14130 else if (cmd->subtype == AT_AddConstraint)
14132 Constraint *con = castNode(Constraint, cmd->def);
14134 con->old_pktable_oid = refRelId;
14135 /* rewriting neither side of a FK */
14136 if (con->contype == CONSTR_FOREIGN &&
14137 !rewrite && tab->rewrite == 0)
14138 TryReuseForeignKey(oldId, con);
14139 con->reset_default_tblspc = true;
14140 cmd->subtype = AT_ReAddConstraint;
14141 tab->subcmds[AT_PASS_OLD_CONSTR] =
14142 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14144 /* recreate any comment on the constraint */
14145 RebuildConstraintComment(tab,
14146 AT_PASS_OLD_CONSTR,
14147 oldId,
14148 rel,
14149 NIL,
14150 con->conname);
14152 else if (cmd->subtype == AT_SetAttNotNull)
14155 * The parser will create AT_AttSetNotNull subcommands for
14156 * columns of PRIMARY KEY indexes/constraints, but we need
14157 * not do anything with them here, because the columns'
14158 * NOT NULL marks will already have been propagated into
14159 * the new table definition.
14162 else
14163 elog(ERROR, "unexpected statement subtype: %d",
14164 (int) cmd->subtype);
14167 else if (IsA(stm, AlterDomainStmt))
14169 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14171 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14173 Constraint *con = castNode(Constraint, stmt->def);
14174 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14176 cmd->subtype = AT_ReAddDomainConstraint;
14177 cmd->def = (Node *) stmt;
14178 tab->subcmds[AT_PASS_OLD_CONSTR] =
14179 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14181 /* recreate any comment on the constraint */
14182 RebuildConstraintComment(tab,
14183 AT_PASS_OLD_CONSTR,
14184 oldId,
14185 NULL,
14186 stmt->typeName,
14187 con->conname);
14189 else
14190 elog(ERROR, "unexpected statement subtype: %d",
14191 (int) stmt->subtype);
14193 else if (IsA(stm, CreateStatsStmt))
14195 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14196 AlterTableCmd *newcmd;
14198 /* keep the statistics object's comment */
14199 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14201 newcmd = makeNode(AlterTableCmd);
14202 newcmd->subtype = AT_ReAddStatistics;
14203 newcmd->def = (Node *) stmt;
14204 tab->subcmds[AT_PASS_MISC] =
14205 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14207 else
14208 elog(ERROR, "unexpected statement type: %d",
14209 (int) nodeTag(stm));
14212 relation_close(rel, NoLock);
14216 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14217 * for a table or domain constraint that is being rebuilt.
14219 * objid is the OID of the constraint.
14220 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14221 * as a string list) for a domain constraint.
14222 * (We could dig that info, as well as the conname, out of the pg_constraint
14223 * entry; but callers already have them so might as well pass them.)
14225 static void
14226 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
14227 Relation rel, List *domname,
14228 const char *conname)
14230 CommentStmt *cmd;
14231 char *comment_str;
14232 AlterTableCmd *newcmd;
14234 /* Look for comment for object wanted, and leave if none */
14235 comment_str = GetComment(objid, ConstraintRelationId, 0);
14236 if (comment_str == NULL)
14237 return;
14239 /* Build CommentStmt node, copying all input data for safety */
14240 cmd = makeNode(CommentStmt);
14241 if (rel)
14243 cmd->objtype = OBJECT_TABCONSTRAINT;
14244 cmd->object = (Node *)
14245 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14246 makeString(pstrdup(RelationGetRelationName(rel))),
14247 makeString(pstrdup(conname)));
14249 else
14251 cmd->objtype = OBJECT_DOMCONSTRAINT;
14252 cmd->object = (Node *)
14253 list_make2(makeTypeNameFromNameList(copyObject(domname)),
14254 makeString(pstrdup(conname)));
14256 cmd->comment = comment_str;
14258 /* Append it to list of commands */
14259 newcmd = makeNode(AlterTableCmd);
14260 newcmd->subtype = AT_ReAddComment;
14261 newcmd->def = (Node *) cmd;
14262 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14266 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14267 * for the real analysis, then mutates the IndexStmt based on that verdict.
14269 static void
14270 TryReuseIndex(Oid oldId, IndexStmt *stmt)
14272 if (CheckIndexCompatible(oldId,
14273 stmt->accessMethod,
14274 stmt->indexParams,
14275 stmt->excludeOpNames))
14277 Relation irel = index_open(oldId, NoLock);
14279 /* If it's a partitioned index, there is no storage to share. */
14280 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14282 stmt->oldNumber = irel->rd_locator.relNumber;
14283 stmt->oldCreateSubid = irel->rd_createSubid;
14284 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14286 index_close(irel, NoLock);
14291 * Subroutine for ATPostAlterTypeParse().
14293 * Stash the old P-F equality operator into the Constraint node, for possible
14294 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14295 * this constraint can be skipped.
14297 static void
14298 TryReuseForeignKey(Oid oldId, Constraint *con)
14300 HeapTuple tup;
14301 Datum adatum;
14302 ArrayType *arr;
14303 Oid *rawarr;
14304 int numkeys;
14305 int i;
14307 Assert(con->contype == CONSTR_FOREIGN);
14308 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14310 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14311 if (!HeapTupleIsValid(tup)) /* should not happen */
14312 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14314 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14315 Anum_pg_constraint_conpfeqop);
14316 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14317 numkeys = ARR_DIMS(arr)[0];
14318 /* test follows the one in ri_FetchConstraintInfo() */
14319 if (ARR_NDIM(arr) != 1 ||
14320 ARR_HASNULL(arr) ||
14321 ARR_ELEMTYPE(arr) != OIDOID)
14322 elog(ERROR, "conpfeqop is not a 1-D Oid array");
14323 rawarr = (Oid *) ARR_DATA_PTR(arr);
14325 /* stash a List of the operator Oids in our Constraint node */
14326 for (i = 0; i < numkeys; i++)
14327 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14329 ReleaseSysCache(tup);
14333 * ALTER COLUMN .. OPTIONS ( ... )
14335 * Returns the address of the modified column
14337 static ObjectAddress
14338 ATExecAlterColumnGenericOptions(Relation rel,
14339 const char *colName,
14340 List *options,
14341 LOCKMODE lockmode)
14343 Relation ftrel;
14344 Relation attrel;
14345 ForeignServer *server;
14346 ForeignDataWrapper *fdw;
14347 HeapTuple tuple;
14348 HeapTuple newtuple;
14349 bool isnull;
14350 Datum repl_val[Natts_pg_attribute];
14351 bool repl_null[Natts_pg_attribute];
14352 bool repl_repl[Natts_pg_attribute];
14353 Datum datum;
14354 Form_pg_foreign_table fttableform;
14355 Form_pg_attribute atttableform;
14356 AttrNumber attnum;
14357 ObjectAddress address;
14359 if (options == NIL)
14360 return InvalidObjectAddress;
14362 /* First, determine FDW validator associated to the foreign table. */
14363 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14364 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14365 if (!HeapTupleIsValid(tuple))
14366 ereport(ERROR,
14367 (errcode(ERRCODE_UNDEFINED_OBJECT),
14368 errmsg("foreign table \"%s\" does not exist",
14369 RelationGetRelationName(rel))));
14370 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14371 server = GetForeignServer(fttableform->ftserver);
14372 fdw = GetForeignDataWrapper(server->fdwid);
14374 table_close(ftrel, AccessShareLock);
14375 ReleaseSysCache(tuple);
14377 attrel = table_open(AttributeRelationId, RowExclusiveLock);
14378 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14379 if (!HeapTupleIsValid(tuple))
14380 ereport(ERROR,
14381 (errcode(ERRCODE_UNDEFINED_COLUMN),
14382 errmsg("column \"%s\" of relation \"%s\" does not exist",
14383 colName, RelationGetRelationName(rel))));
14385 /* Prevent them from altering a system attribute */
14386 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14387 attnum = atttableform->attnum;
14388 if (attnum <= 0)
14389 ereport(ERROR,
14390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14391 errmsg("cannot alter system column \"%s\"", colName)));
14394 /* Initialize buffers for new tuple values */
14395 memset(repl_val, 0, sizeof(repl_val));
14396 memset(repl_null, false, sizeof(repl_null));
14397 memset(repl_repl, false, sizeof(repl_repl));
14399 /* Extract the current options */
14400 datum = SysCacheGetAttr(ATTNAME,
14401 tuple,
14402 Anum_pg_attribute_attfdwoptions,
14403 &isnull);
14404 if (isnull)
14405 datum = PointerGetDatum(NULL);
14407 /* Transform the options */
14408 datum = transformGenericOptions(AttributeRelationId,
14409 datum,
14410 options,
14411 fdw->fdwvalidator);
14413 if (PointerIsValid(DatumGetPointer(datum)))
14414 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14415 else
14416 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14418 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14420 /* Everything looks good - update the tuple */
14422 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14423 repl_val, repl_null, repl_repl);
14425 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14427 InvokeObjectPostAlterHook(RelationRelationId,
14428 RelationGetRelid(rel),
14429 atttableform->attnum);
14430 ObjectAddressSubSet(address, RelationRelationId,
14431 RelationGetRelid(rel), attnum);
14433 ReleaseSysCache(tuple);
14435 table_close(attrel, RowExclusiveLock);
14437 heap_freetuple(newtuple);
14439 return address;
14443 * ALTER TABLE OWNER
14445 * recursing is true if we are recursing from a table to its indexes,
14446 * sequences, or toast table. We don't allow the ownership of those things to
14447 * be changed separately from the parent table. Also, we can skip permission
14448 * checks (this is necessary not just an optimization, else we'd fail to
14449 * handle toast tables properly).
14451 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14452 * free-standing composite type.
14454 void
14455 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14457 Relation target_rel;
14458 Relation class_rel;
14459 HeapTuple tuple;
14460 Form_pg_class tuple_class;
14463 * Get exclusive lock till end of transaction on the target table. Use
14464 * relation_open so that we can work on indexes and sequences.
14466 target_rel = relation_open(relationOid, lockmode);
14468 /* Get its pg_class tuple, too */
14469 class_rel = table_open(RelationRelationId, RowExclusiveLock);
14471 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14472 if (!HeapTupleIsValid(tuple))
14473 elog(ERROR, "cache lookup failed for relation %u", relationOid);
14474 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14476 /* Can we change the ownership of this tuple? */
14477 switch (tuple_class->relkind)
14479 case RELKIND_RELATION:
14480 case RELKIND_VIEW:
14481 case RELKIND_MATVIEW:
14482 case RELKIND_FOREIGN_TABLE:
14483 case RELKIND_PARTITIONED_TABLE:
14484 /* ok to change owner */
14485 break;
14486 case RELKIND_INDEX:
14487 if (!recursing)
14490 * Because ALTER INDEX OWNER used to be allowed, and in fact
14491 * is generated by old versions of pg_dump, we give a warning
14492 * and do nothing rather than erroring out. Also, to avoid
14493 * unnecessary chatter while restoring those old dumps, say
14494 * nothing at all if the command would be a no-op anyway.
14496 if (tuple_class->relowner != newOwnerId)
14497 ereport(WARNING,
14498 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14499 errmsg("cannot change owner of index \"%s\"",
14500 NameStr(tuple_class->relname)),
14501 errhint("Change the ownership of the index's table instead.")));
14502 /* quick hack to exit via the no-op path */
14503 newOwnerId = tuple_class->relowner;
14505 break;
14506 case RELKIND_PARTITIONED_INDEX:
14507 if (recursing)
14508 break;
14509 ereport(ERROR,
14510 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14511 errmsg("cannot change owner of index \"%s\"",
14512 NameStr(tuple_class->relname)),
14513 errhint("Change the ownership of the index's table instead.")));
14514 break;
14515 case RELKIND_SEQUENCE:
14516 if (!recursing &&
14517 tuple_class->relowner != newOwnerId)
14519 /* if it's an owned sequence, disallow changing it by itself */
14520 Oid tableId;
14521 int32 colId;
14523 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14524 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14525 ereport(ERROR,
14526 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14527 errmsg("cannot change owner of sequence \"%s\"",
14528 NameStr(tuple_class->relname)),
14529 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14530 NameStr(tuple_class->relname),
14531 get_rel_name(tableId))));
14533 break;
14534 case RELKIND_COMPOSITE_TYPE:
14535 if (recursing)
14536 break;
14537 ereport(ERROR,
14538 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14539 errmsg("\"%s\" is a composite type",
14540 NameStr(tuple_class->relname)),
14541 /* translator: %s is an SQL ALTER command */
14542 errhint("Use %s instead.",
14543 "ALTER TYPE")));
14544 break;
14545 case RELKIND_TOASTVALUE:
14546 if (recursing)
14547 break;
14548 /* FALL THRU */
14549 default:
14550 ereport(ERROR,
14551 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14552 errmsg("cannot change owner of relation \"%s\"",
14553 NameStr(tuple_class->relname)),
14554 errdetail_relkind_not_supported(tuple_class->relkind)));
14558 * If the new owner is the same as the existing owner, consider the
14559 * command to have succeeded. This is for dump restoration purposes.
14561 if (tuple_class->relowner != newOwnerId)
14563 Datum repl_val[Natts_pg_class];
14564 bool repl_null[Natts_pg_class];
14565 bool repl_repl[Natts_pg_class];
14566 Acl *newAcl;
14567 Datum aclDatum;
14568 bool isNull;
14569 HeapTuple newtuple;
14571 /* skip permission checks when recursing to index or toast table */
14572 if (!recursing)
14574 /* Superusers can always do it */
14575 if (!superuser())
14577 Oid namespaceOid = tuple_class->relnamespace;
14578 AclResult aclresult;
14580 /* Otherwise, must be owner of the existing object */
14581 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
14582 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
14583 RelationGetRelationName(target_rel));
14585 /* Must be able to become new owner */
14586 check_can_set_role(GetUserId(), newOwnerId);
14588 /* New owner must have CREATE privilege on namespace */
14589 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14590 ACL_CREATE);
14591 if (aclresult != ACLCHECK_OK)
14592 aclcheck_error(aclresult, OBJECT_SCHEMA,
14593 get_namespace_name(namespaceOid));
14597 memset(repl_null, false, sizeof(repl_null));
14598 memset(repl_repl, false, sizeof(repl_repl));
14600 repl_repl[Anum_pg_class_relowner - 1] = true;
14601 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
14604 * Determine the modified ACL for the new owner. This is only
14605 * necessary when the ACL is non-null.
14607 aclDatum = SysCacheGetAttr(RELOID, tuple,
14608 Anum_pg_class_relacl,
14609 &isNull);
14610 if (!isNull)
14612 newAcl = aclnewowner(DatumGetAclP(aclDatum),
14613 tuple_class->relowner, newOwnerId);
14614 repl_repl[Anum_pg_class_relacl - 1] = true;
14615 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
14618 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
14620 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
14622 heap_freetuple(newtuple);
14625 * We must similarly update any per-column ACLs to reflect the new
14626 * owner; for neatness reasons that's split out as a subroutine.
14628 change_owner_fix_column_acls(relationOid,
14629 tuple_class->relowner,
14630 newOwnerId);
14633 * Update owner dependency reference, if any. A composite type has
14634 * none, because it's tracked for the pg_type entry instead of here;
14635 * indexes and TOAST tables don't have their own entries either.
14637 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
14638 tuple_class->relkind != RELKIND_INDEX &&
14639 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
14640 tuple_class->relkind != RELKIND_TOASTVALUE)
14641 changeDependencyOnOwner(RelationRelationId, relationOid,
14642 newOwnerId);
14645 * Also change the ownership of the table's row type, if it has one
14647 if (OidIsValid(tuple_class->reltype))
14648 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
14651 * If we are operating on a table or materialized view, also change
14652 * the ownership of any indexes and sequences that belong to the
14653 * relation, as well as its toast table (if it has one).
14655 if (tuple_class->relkind == RELKIND_RELATION ||
14656 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
14657 tuple_class->relkind == RELKIND_MATVIEW ||
14658 tuple_class->relkind == RELKIND_TOASTVALUE)
14660 List *index_oid_list;
14661 ListCell *i;
14663 /* Find all the indexes belonging to this relation */
14664 index_oid_list = RelationGetIndexList(target_rel);
14666 /* For each index, recursively change its ownership */
14667 foreach(i, index_oid_list)
14668 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14670 list_free(index_oid_list);
14673 /* If it has a toast table, recurse to change its ownership */
14674 if (tuple_class->reltoastrelid != InvalidOid)
14675 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14676 true, lockmode);
14678 /* If it has dependent sequences, recurse to change them too */
14679 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
14682 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14684 ReleaseSysCache(tuple);
14685 table_close(class_rel, RowExclusiveLock);
14686 relation_close(target_rel, NoLock);
14690 * change_owner_fix_column_acls
14692 * Helper function for ATExecChangeOwner. Scan the columns of the table
14693 * and fix any non-null column ACLs to reflect the new owner.
14695 static void
14696 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
14698 Relation attRelation;
14699 SysScanDesc scan;
14700 ScanKeyData key[1];
14701 HeapTuple attributeTuple;
14703 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
14704 ScanKeyInit(&key[0],
14705 Anum_pg_attribute_attrelid,
14706 BTEqualStrategyNumber, F_OIDEQ,
14707 ObjectIdGetDatum(relationOid));
14708 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
14709 true, NULL, 1, key);
14710 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14712 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14713 Datum repl_val[Natts_pg_attribute];
14714 bool repl_null[Natts_pg_attribute];
14715 bool repl_repl[Natts_pg_attribute];
14716 Acl *newAcl;
14717 Datum aclDatum;
14718 bool isNull;
14719 HeapTuple newtuple;
14721 /* Ignore dropped columns */
14722 if (att->attisdropped)
14723 continue;
14725 aclDatum = heap_getattr(attributeTuple,
14726 Anum_pg_attribute_attacl,
14727 RelationGetDescr(attRelation),
14728 &isNull);
14729 /* Null ACLs do not require changes */
14730 if (isNull)
14731 continue;
14733 memset(repl_null, false, sizeof(repl_null));
14734 memset(repl_repl, false, sizeof(repl_repl));
14736 newAcl = aclnewowner(DatumGetAclP(aclDatum),
14737 oldOwnerId, newOwnerId);
14738 repl_repl[Anum_pg_attribute_attacl - 1] = true;
14739 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
14741 newtuple = heap_modify_tuple(attributeTuple,
14742 RelationGetDescr(attRelation),
14743 repl_val, repl_null, repl_repl);
14745 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14747 heap_freetuple(newtuple);
14749 systable_endscan(scan);
14750 table_close(attRelation, RowExclusiveLock);
14754 * change_owner_recurse_to_sequences
14756 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14757 * for sequences that are dependent on serial columns, and changes their
14758 * ownership.
14760 static void
14761 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
14763 Relation depRel;
14764 SysScanDesc scan;
14765 ScanKeyData key[2];
14766 HeapTuple tup;
14769 * SERIAL sequences are those having an auto dependency on one of the
14770 * table's columns (we don't care *which* column, exactly).
14772 depRel = table_open(DependRelationId, AccessShareLock);
14774 ScanKeyInit(&key[0],
14775 Anum_pg_depend_refclassid,
14776 BTEqualStrategyNumber, F_OIDEQ,
14777 ObjectIdGetDatum(RelationRelationId));
14778 ScanKeyInit(&key[1],
14779 Anum_pg_depend_refobjid,
14780 BTEqualStrategyNumber, F_OIDEQ,
14781 ObjectIdGetDatum(relationOid));
14782 /* we leave refobjsubid unspecified */
14784 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14785 NULL, 2, key);
14787 while (HeapTupleIsValid(tup = systable_getnext(scan)))
14789 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14790 Relation seqRel;
14792 /* skip dependencies other than auto dependencies on columns */
14793 if (depForm->refobjsubid == 0 ||
14794 depForm->classid != RelationRelationId ||
14795 depForm->objsubid != 0 ||
14796 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14797 continue;
14799 /* Use relation_open just in case it's an index */
14800 seqRel = relation_open(depForm->objid, lockmode);
14802 /* skip non-sequence relations */
14803 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14805 /* No need to keep the lock */
14806 relation_close(seqRel, lockmode);
14807 continue;
14810 /* We don't need to close the sequence while we alter it. */
14811 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14813 /* Now we can close it. Keep the lock till end of transaction. */
14814 relation_close(seqRel, NoLock);
14817 systable_endscan(scan);
14819 relation_close(depRel, AccessShareLock);
14823 * ALTER TABLE CLUSTER ON
14825 * The only thing we have to do is to change the indisclustered bits.
14827 * Return the address of the new clustering index.
14829 static ObjectAddress
14830 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
14832 Oid indexOid;
14833 ObjectAddress address;
14835 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14837 if (!OidIsValid(indexOid))
14838 ereport(ERROR,
14839 (errcode(ERRCODE_UNDEFINED_OBJECT),
14840 errmsg("index \"%s\" for table \"%s\" does not exist",
14841 indexName, RelationGetRelationName(rel))));
14843 /* Check index is valid to cluster on */
14844 check_index_is_clusterable(rel, indexOid, lockmode);
14846 /* And do the work */
14847 mark_index_clustered(rel, indexOid, false);
14849 ObjectAddressSet(address,
14850 RelationRelationId, indexOid);
14852 return address;
14856 * ALTER TABLE SET WITHOUT CLUSTER
14858 * We have to find any indexes on the table that have indisclustered bit
14859 * set and turn it off.
14861 static void
14862 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
14864 mark_index_clustered(rel, InvalidOid, false);
14868 * Preparation phase for SET ACCESS METHOD
14870 * Check that access method exists. If it is the same as the table's current
14871 * access method, it is a no-op. Otherwise, a table rewrite is necessary.
14873 static void
14874 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
14876 Oid amoid;
14878 /* Check that the table access method exists */
14879 amoid = get_table_am_oid(amname, false);
14881 if (rel->rd_rel->relam == amoid)
14882 return;
14884 /* Save info for Phase 3 to do the real work */
14885 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
14886 tab->newAccessMethod = amoid;
14890 * ALTER TABLE SET TABLESPACE
14892 static void
14893 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
14895 Oid tablespaceId;
14897 /* Check that the tablespace exists */
14898 tablespaceId = get_tablespace_oid(tablespacename, false);
14900 /* Check permissions except when moving to database's default */
14901 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14903 AclResult aclresult;
14905 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
14906 if (aclresult != ACLCHECK_OK)
14907 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
14910 /* Save info for Phase 3 to do the real work */
14911 if (OidIsValid(tab->newTableSpace))
14912 ereport(ERROR,
14913 (errcode(ERRCODE_SYNTAX_ERROR),
14914 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14916 tab->newTableSpace = tablespaceId;
14920 * Set, reset, or replace reloptions.
14922 static void
14923 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
14924 LOCKMODE lockmode)
14926 Oid relid;
14927 Relation pgclass;
14928 HeapTuple tuple;
14929 HeapTuple newtuple;
14930 Datum datum;
14931 bool isnull;
14932 Datum newOptions;
14933 Datum repl_val[Natts_pg_class];
14934 bool repl_null[Natts_pg_class];
14935 bool repl_repl[Natts_pg_class];
14936 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14938 if (defList == NIL && operation != AT_ReplaceRelOptions)
14939 return; /* nothing to do */
14941 pgclass = table_open(RelationRelationId, RowExclusiveLock);
14943 /* Fetch heap tuple */
14944 relid = RelationGetRelid(rel);
14945 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14946 if (!HeapTupleIsValid(tuple))
14947 elog(ERROR, "cache lookup failed for relation %u", relid);
14949 if (operation == AT_ReplaceRelOptions)
14952 * If we're supposed to replace the reloptions list, we just pretend
14953 * there were none before.
14955 datum = (Datum) 0;
14956 isnull = true;
14958 else
14960 /* Get the old reloptions */
14961 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14962 &isnull);
14965 /* Generate new proposed reloptions (text array) */
14966 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14967 defList, NULL, validnsps, false,
14968 operation == AT_ResetRelOptions);
14970 /* Validate */
14971 switch (rel->rd_rel->relkind)
14973 case RELKIND_RELATION:
14974 case RELKIND_TOASTVALUE:
14975 case RELKIND_MATVIEW:
14976 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
14977 break;
14978 case RELKIND_PARTITIONED_TABLE:
14979 (void) partitioned_table_reloptions(newOptions, true);
14980 break;
14981 case RELKIND_VIEW:
14982 (void) view_reloptions(newOptions, true);
14983 break;
14984 case RELKIND_INDEX:
14985 case RELKIND_PARTITIONED_INDEX:
14986 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
14987 break;
14988 default:
14989 ereport(ERROR,
14990 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14991 errmsg("cannot set options for relation \"%s\"",
14992 RelationGetRelationName(rel)),
14993 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
14994 break;
14997 /* Special-case validation of view options */
14998 if (rel->rd_rel->relkind == RELKIND_VIEW)
15000 Query *view_query = get_view_query(rel);
15001 List *view_options = untransformRelOptions(newOptions);
15002 ListCell *cell;
15003 bool check_option = false;
15005 foreach(cell, view_options)
15007 DefElem *defel = (DefElem *) lfirst(cell);
15009 if (strcmp(defel->defname, "check_option") == 0)
15010 check_option = true;
15014 * If the check option is specified, look to see if the view is
15015 * actually auto-updatable or not.
15017 if (check_option)
15019 const char *view_updatable_error =
15020 view_query_is_auto_updatable(view_query, true);
15022 if (view_updatable_error)
15023 ereport(ERROR,
15024 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15025 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15026 errhint("%s", _(view_updatable_error))));
15031 * All we need do here is update the pg_class row; the new options will be
15032 * propagated into relcaches during post-commit cache inval.
15034 memset(repl_val, 0, sizeof(repl_val));
15035 memset(repl_null, false, sizeof(repl_null));
15036 memset(repl_repl, false, sizeof(repl_repl));
15038 if (newOptions != (Datum) 0)
15039 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15040 else
15041 repl_null[Anum_pg_class_reloptions - 1] = true;
15043 repl_repl[Anum_pg_class_reloptions - 1] = true;
15045 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15046 repl_val, repl_null, repl_repl);
15048 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15050 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15052 heap_freetuple(newtuple);
15054 ReleaseSysCache(tuple);
15056 /* repeat the whole exercise for the toast table, if there's one */
15057 if (OidIsValid(rel->rd_rel->reltoastrelid))
15059 Relation toastrel;
15060 Oid toastid = rel->rd_rel->reltoastrelid;
15062 toastrel = table_open(toastid, lockmode);
15064 /* Fetch heap tuple */
15065 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15066 if (!HeapTupleIsValid(tuple))
15067 elog(ERROR, "cache lookup failed for relation %u", toastid);
15069 if (operation == AT_ReplaceRelOptions)
15072 * If we're supposed to replace the reloptions list, we just
15073 * pretend there were none before.
15075 datum = (Datum) 0;
15076 isnull = true;
15078 else
15080 /* Get the old reloptions */
15081 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15082 &isnull);
15085 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15086 defList, "toast", validnsps, false,
15087 operation == AT_ResetRelOptions);
15089 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15091 memset(repl_val, 0, sizeof(repl_val));
15092 memset(repl_null, false, sizeof(repl_null));
15093 memset(repl_repl, false, sizeof(repl_repl));
15095 if (newOptions != (Datum) 0)
15096 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15097 else
15098 repl_null[Anum_pg_class_reloptions - 1] = true;
15100 repl_repl[Anum_pg_class_reloptions - 1] = true;
15102 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15103 repl_val, repl_null, repl_repl);
15105 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15107 InvokeObjectPostAlterHookArg(RelationRelationId,
15108 RelationGetRelid(toastrel), 0,
15109 InvalidOid, true);
15111 heap_freetuple(newtuple);
15113 ReleaseSysCache(tuple);
15115 table_close(toastrel, NoLock);
15118 table_close(pgclass, RowExclusiveLock);
15122 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15123 * rewriting to be done, so we just want to copy the data as fast as possible.
15125 static void
15126 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15128 Relation rel;
15129 Oid reltoastrelid;
15130 RelFileNumber newrelfilenumber;
15131 RelFileLocator newrlocator;
15132 List *reltoastidxids = NIL;
15133 ListCell *lc;
15136 * Need lock here in case we are recursing to toast table or index
15138 rel = relation_open(tableOid, lockmode);
15140 /* Check first if relation can be moved to new tablespace */
15141 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15143 InvokeObjectPostAlterHook(RelationRelationId,
15144 RelationGetRelid(rel), 0);
15145 relation_close(rel, NoLock);
15146 return;
15149 reltoastrelid = rel->rd_rel->reltoastrelid;
15150 /* Fetch the list of indexes on toast relation if necessary */
15151 if (OidIsValid(reltoastrelid))
15153 Relation toastRel = relation_open(reltoastrelid, lockmode);
15155 reltoastidxids = RelationGetIndexList(toastRel);
15156 relation_close(toastRel, lockmode);
15160 * Relfilenumbers are not unique in databases across tablespaces, so we
15161 * need to allocate a new one in the new tablespace.
15163 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15164 rel->rd_rel->relpersistence);
15166 /* Open old and new relation */
15167 newrlocator = rel->rd_locator;
15168 newrlocator.relNumber = newrelfilenumber;
15169 newrlocator.spcOid = newTableSpace;
15171 /* hand off to AM to actually create new rel storage and copy the data */
15172 if (rel->rd_rel->relkind == RELKIND_INDEX)
15174 index_copy_data(rel, newrlocator);
15176 else
15178 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15179 table_relation_copy_data(rel, &newrlocator);
15183 * Update the pg_class row.
15185 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15186 * executed on pg_class or its indexes (the above copy wouldn't contain
15187 * the updated pg_class entry), but that's forbidden with
15188 * CheckRelationTableSpaceMove().
15190 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15192 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15194 RelationAssumeNewRelfilelocator(rel);
15196 relation_close(rel, NoLock);
15198 /* Make sure the reltablespace change is visible */
15199 CommandCounterIncrement();
15201 /* Move associated toast relation and/or indexes, too */
15202 if (OidIsValid(reltoastrelid))
15203 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15204 foreach(lc, reltoastidxids)
15205 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15207 /* Clean up */
15208 list_free(reltoastidxids);
15212 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15213 * storage that have an interest in preserving tablespace.
15215 * Since these have no storage the tablespace can be updated with a simple
15216 * metadata only operation to update the tablespace.
15218 static void
15219 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15222 * Shouldn't be called on relations having storage; these are processed in
15223 * phase 3.
15225 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15227 /* check if relation can be moved to its new tablespace */
15228 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15230 InvokeObjectPostAlterHook(RelationRelationId,
15231 RelationGetRelid(rel),
15233 return;
15236 /* Update can be done, so change reltablespace */
15237 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15239 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15241 /* Make sure the reltablespace change is visible */
15242 CommandCounterIncrement();
15246 * Alter Table ALL ... SET TABLESPACE
15248 * Allows a user to move all objects of some type in a given tablespace in the
15249 * current database to another tablespace. Objects can be chosen based on the
15250 * owner of the object also, to allow users to move only their objects.
15251 * The user must have CREATE rights on the new tablespace, as usual. The main
15252 * permissions handling is done by the lower-level table move function.
15254 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15255 * lock can't be acquired then we ereport(ERROR).
15258 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15260 List *relations = NIL;
15261 ListCell *l;
15262 ScanKeyData key[1];
15263 Relation rel;
15264 TableScanDesc scan;
15265 HeapTuple tuple;
15266 Oid orig_tablespaceoid;
15267 Oid new_tablespaceoid;
15268 List *role_oids = roleSpecsToIds(stmt->roles);
15270 /* Ensure we were not asked to move something we can't */
15271 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15272 stmt->objtype != OBJECT_MATVIEW)
15273 ereport(ERROR,
15274 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15275 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15277 /* Get the orig and new tablespace OIDs */
15278 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15279 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15281 /* Can't move shared relations in to or out of pg_global */
15282 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15283 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15284 new_tablespaceoid == GLOBALTABLESPACE_OID)
15285 ereport(ERROR,
15286 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15287 errmsg("cannot move relations in to or out of pg_global tablespace")));
15290 * Must have CREATE rights on the new tablespace, unless it is the
15291 * database default tablespace (which all users implicitly have CREATE
15292 * rights on).
15294 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15296 AclResult aclresult;
15298 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15299 ACL_CREATE);
15300 if (aclresult != ACLCHECK_OK)
15301 aclcheck_error(aclresult, OBJECT_TABLESPACE,
15302 get_tablespace_name(new_tablespaceoid));
15306 * Now that the checks are done, check if we should set either to
15307 * InvalidOid because it is our database's default tablespace.
15309 if (orig_tablespaceoid == MyDatabaseTableSpace)
15310 orig_tablespaceoid = InvalidOid;
15312 if (new_tablespaceoid == MyDatabaseTableSpace)
15313 new_tablespaceoid = InvalidOid;
15315 /* no-op */
15316 if (orig_tablespaceoid == new_tablespaceoid)
15317 return new_tablespaceoid;
15320 * Walk the list of objects in the tablespace and move them. This will
15321 * only find objects in our database, of course.
15323 ScanKeyInit(&key[0],
15324 Anum_pg_class_reltablespace,
15325 BTEqualStrategyNumber, F_OIDEQ,
15326 ObjectIdGetDatum(orig_tablespaceoid));
15328 rel = table_open(RelationRelationId, AccessShareLock);
15329 scan = table_beginscan_catalog(rel, 1, key);
15330 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15332 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15333 Oid relOid = relForm->oid;
15336 * Do not move objects in pg_catalog as part of this, if an admin
15337 * really wishes to do so, they can issue the individual ALTER
15338 * commands directly.
15340 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15341 * (TOAST will be moved with the main table).
15343 if (IsCatalogNamespace(relForm->relnamespace) ||
15344 relForm->relisshared ||
15345 isAnyTempNamespace(relForm->relnamespace) ||
15346 IsToastNamespace(relForm->relnamespace))
15347 continue;
15349 /* Only move the object type requested */
15350 if ((stmt->objtype == OBJECT_TABLE &&
15351 relForm->relkind != RELKIND_RELATION &&
15352 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15353 (stmt->objtype == OBJECT_INDEX &&
15354 relForm->relkind != RELKIND_INDEX &&
15355 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15356 (stmt->objtype == OBJECT_MATVIEW &&
15357 relForm->relkind != RELKIND_MATVIEW))
15358 continue;
15360 /* Check if we are only moving objects owned by certain roles */
15361 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15362 continue;
15365 * Handle permissions-checking here since we are locking the tables
15366 * and also to avoid doing a bunch of work only to fail part-way. Note
15367 * that permissions will also be checked by AlterTableInternal().
15369 * Caller must be considered an owner on the table to move it.
15371 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15372 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15373 NameStr(relForm->relname));
15375 if (stmt->nowait &&
15376 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15377 ereport(ERROR,
15378 (errcode(ERRCODE_OBJECT_IN_USE),
15379 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15380 get_namespace_name(relForm->relnamespace),
15381 NameStr(relForm->relname))));
15382 else
15383 LockRelationOid(relOid, AccessExclusiveLock);
15385 /* Add to our list of objects to move */
15386 relations = lappend_oid(relations, relOid);
15389 table_endscan(scan);
15390 table_close(rel, AccessShareLock);
15392 if (relations == NIL)
15393 ereport(NOTICE,
15394 (errcode(ERRCODE_NO_DATA_FOUND),
15395 errmsg("no matching relations in tablespace \"%s\" found",
15396 orig_tablespaceoid == InvalidOid ? "(database default)" :
15397 get_tablespace_name(orig_tablespaceoid))));
15399 /* Everything is locked, loop through and move all of the relations. */
15400 foreach(l, relations)
15402 List *cmds = NIL;
15403 AlterTableCmd *cmd = makeNode(AlterTableCmd);
15405 cmd->subtype = AT_SetTableSpace;
15406 cmd->name = stmt->new_tablespacename;
15408 cmds = lappend(cmds, cmd);
15410 EventTriggerAlterTableStart((Node *) stmt);
15411 /* OID is set by AlterTableInternal */
15412 AlterTableInternal(lfirst_oid(l), cmds, false);
15413 EventTriggerAlterTableEnd();
15416 return new_tablespaceoid;
15419 static void
15420 index_copy_data(Relation rel, RelFileLocator newrlocator)
15422 SMgrRelation dstrel;
15424 dstrel = smgropen(newrlocator, rel->rd_backend);
15427 * Since we copy the file directly without looking at the shared buffers,
15428 * we'd better first flush out any pages of the source relation that are
15429 * in shared buffers. We assume no new changes will be made while we are
15430 * holding exclusive lock on the rel.
15432 FlushRelationBuffers(rel);
15435 * Create and copy all forks of the relation, and schedule unlinking of
15436 * old physical files.
15438 * NOTE: any conflict in relfilenumber value will be caught in
15439 * RelationCreateStorage().
15441 RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15443 /* copy main fork */
15444 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
15445 rel->rd_rel->relpersistence);
15447 /* copy those extra forks that exist */
15448 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15449 forkNum <= MAX_FORKNUM; forkNum++)
15451 if (smgrexists(RelationGetSmgr(rel), forkNum))
15453 smgrcreate(dstrel, forkNum, false);
15456 * WAL log creation if the relation is persistent, or this is the
15457 * init fork of an unlogged relation.
15459 if (RelationIsPermanent(rel) ||
15460 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
15461 forkNum == INIT_FORKNUM))
15462 log_smgrcreate(&newrlocator, forkNum);
15463 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
15464 rel->rd_rel->relpersistence);
15468 /* drop old relation, and close new one */
15469 RelationDropStorage(rel);
15470 smgrclose(dstrel);
15474 * ALTER TABLE ENABLE/DISABLE TRIGGER
15476 * We just pass this off to trigger.c.
15478 static void
15479 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
15480 char fires_when, bool skip_system, bool recurse,
15481 LOCKMODE lockmode)
15483 EnableDisableTrigger(rel, trigname, InvalidOid,
15484 fires_when, skip_system, recurse,
15485 lockmode);
15487 InvokeObjectPostAlterHook(RelationRelationId,
15488 RelationGetRelid(rel), 0);
15492 * ALTER TABLE ENABLE/DISABLE RULE
15494 * We just pass this off to rewriteDefine.c.
15496 static void
15497 ATExecEnableDisableRule(Relation rel, const char *rulename,
15498 char fires_when, LOCKMODE lockmode)
15500 EnableDisableRule(rel, rulename, fires_when);
15502 InvokeObjectPostAlterHook(RelationRelationId,
15503 RelationGetRelid(rel), 0);
15507 * ALTER TABLE INHERIT
15509 * Add a parent to the child's parents. This verifies that all the columns and
15510 * check constraints of the parent appear in the child and that they have the
15511 * same data types and expressions.
15513 static void
15514 ATPrepAddInherit(Relation child_rel)
15516 if (child_rel->rd_rel->reloftype)
15517 ereport(ERROR,
15518 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15519 errmsg("cannot change inheritance of typed table")));
15521 if (child_rel->rd_rel->relispartition)
15522 ereport(ERROR,
15523 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15524 errmsg("cannot change inheritance of a partition")));
15526 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15527 ereport(ERROR,
15528 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15529 errmsg("cannot change inheritance of partitioned table")));
15533 * Return the address of the new parent relation.
15535 static ObjectAddress
15536 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
15538 Relation parent_rel;
15539 List *children;
15540 ObjectAddress address;
15541 const char *trigger_name;
15544 * A self-exclusive lock is needed here. See the similar case in
15545 * MergeAttributes() for a full explanation.
15547 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
15550 * Must be owner of both parent and child -- child was checked by
15551 * ATSimplePermissions call in ATPrepCmd
15553 ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
15555 /* Permanent rels cannot inherit from temporary ones */
15556 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15557 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
15558 ereport(ERROR,
15559 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15560 errmsg("cannot inherit from temporary relation \"%s\"",
15561 RelationGetRelationName(parent_rel))));
15563 /* If parent rel is temp, it must belong to this session */
15564 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15565 !parent_rel->rd_islocaltemp)
15566 ereport(ERROR,
15567 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15568 errmsg("cannot inherit from temporary relation of another session")));
15570 /* Ditto for the child */
15571 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15572 !child_rel->rd_islocaltemp)
15573 ereport(ERROR,
15574 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15575 errmsg("cannot inherit to temporary relation of another session")));
15577 /* Prevent partitioned tables from becoming inheritance parents */
15578 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15579 ereport(ERROR,
15580 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15581 errmsg("cannot inherit from partitioned table \"%s\"",
15582 parent->relname)));
15584 /* Likewise for partitions */
15585 if (parent_rel->rd_rel->relispartition)
15586 ereport(ERROR,
15587 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15588 errmsg("cannot inherit from a partition")));
15591 * Prevent circularity by seeing if proposed parent inherits from child.
15592 * (In particular, this disallows making a rel inherit from itself.)
15594 * This is not completely bulletproof because of race conditions: in
15595 * multi-level inheritance trees, someone else could concurrently be
15596 * making another inheritance link that closes the loop but does not join
15597 * either of the rels we have locked. Preventing that seems to require
15598 * exclusive locks on the entire inheritance tree, which is a cure worse
15599 * than the disease. find_all_inheritors() will cope with circularity
15600 * anyway, so don't sweat it too much.
15602 * We use weakest lock we can on child's children, namely AccessShareLock.
15604 children = find_all_inheritors(RelationGetRelid(child_rel),
15605 AccessShareLock, NULL);
15607 if (list_member_oid(children, RelationGetRelid(parent_rel)))
15608 ereport(ERROR,
15609 (errcode(ERRCODE_DUPLICATE_TABLE),
15610 errmsg("circular inheritance not allowed"),
15611 errdetail("\"%s\" is already a child of \"%s\".",
15612 parent->relname,
15613 RelationGetRelationName(child_rel))));
15616 * If child_rel has row-level triggers with transition tables, we
15617 * currently don't allow it to become an inheritance child. See also
15618 * prohibitions in ATExecAttachPartition() and CreateTrigger().
15620 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
15621 if (trigger_name != NULL)
15622 ereport(ERROR,
15623 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15624 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15625 trigger_name, RelationGetRelationName(child_rel)),
15626 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15628 /* OK to create inheritance */
15629 CreateInheritance(child_rel, parent_rel);
15632 * If parent_rel has a primary key, then child_rel has not-null
15633 * constraints that make these columns as non nullable. Make those
15634 * constraints as inherited.
15636 ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
15638 ObjectAddressSet(address, RelationRelationId,
15639 RelationGetRelid(parent_rel));
15641 /* keep our lock on the parent relation until commit */
15642 table_close(parent_rel, NoLock);
15644 return address;
15648 * CreateInheritance
15649 * Catalog manipulation portion of creating inheritance between a child
15650 * table and a parent table.
15652 * Common to ATExecAddInherit() and ATExecAttachPartition().
15654 static void
15655 CreateInheritance(Relation child_rel, Relation parent_rel)
15657 Relation catalogRelation;
15658 SysScanDesc scan;
15659 ScanKeyData key;
15660 HeapTuple inheritsTuple;
15661 int32 inhseqno;
15663 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15664 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15667 * Check for duplicates in the list of parents, and determine the highest
15668 * inhseqno already present; we'll use the next one for the new parent.
15669 * Also, if proposed child is a partition, it cannot already be
15670 * inheriting.
15672 * Note: we do not reject the case where the child already inherits from
15673 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15675 ScanKeyInit(&key,
15676 Anum_pg_inherits_inhrelid,
15677 BTEqualStrategyNumber, F_OIDEQ,
15678 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15679 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15680 true, NULL, 1, &key);
15682 /* inhseqno sequences start at 1 */
15683 inhseqno = 0;
15684 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15686 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15688 if (inh->inhparent == RelationGetRelid(parent_rel))
15689 ereport(ERROR,
15690 (errcode(ERRCODE_DUPLICATE_TABLE),
15691 errmsg("relation \"%s\" would be inherited from more than once",
15692 RelationGetRelationName(parent_rel))));
15694 if (inh->inhseqno > inhseqno)
15695 inhseqno = inh->inhseqno;
15697 systable_endscan(scan);
15699 /* Match up the columns and bump attinhcount as needed */
15700 MergeAttributesIntoExisting(child_rel, parent_rel);
15702 /* Match up the constraints and bump coninhcount as needed */
15703 MergeConstraintsIntoExisting(child_rel, parent_rel);
15706 * OK, it looks valid. Make the catalog entries that show inheritance.
15708 StoreCatalogInheritance1(RelationGetRelid(child_rel),
15709 RelationGetRelid(parent_rel),
15710 inhseqno + 1,
15711 catalogRelation,
15712 parent_rel->rd_rel->relkind ==
15713 RELKIND_PARTITIONED_TABLE);
15715 /* Now we're done with pg_inherits */
15716 table_close(catalogRelation, RowExclusiveLock);
15720 * Obtain the source-text form of the constraint expression for a check
15721 * constraint, given its pg_constraint tuple
15723 static char *
15724 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
15726 Form_pg_constraint con;
15727 bool isnull;
15728 Datum attr;
15729 Datum expr;
15731 con = (Form_pg_constraint) GETSTRUCT(contup);
15732 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15733 if (isnull)
15734 elog(ERROR, "null conbin for constraint %u", con->oid);
15736 expr = DirectFunctionCall2(pg_get_expr, attr,
15737 ObjectIdGetDatum(con->conrelid));
15738 return TextDatumGetCString(expr);
15742 * Determine whether two check constraints are functionally equivalent
15744 * The test we apply is to see whether they reverse-compile to the same
15745 * source string. This insulates us from issues like whether attributes
15746 * have the same physical column numbers in parent and child relations.
15748 static bool
15749 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
15751 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
15752 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
15754 if (acon->condeferrable != bcon->condeferrable ||
15755 acon->condeferred != bcon->condeferred ||
15756 strcmp(decompile_conbin(a, tupleDesc),
15757 decompile_conbin(b, tupleDesc)) != 0)
15758 return false;
15759 else
15760 return true;
15764 * Check columns in child table match up with columns in parent, and increment
15765 * their attinhcount.
15767 * Called by CreateInheritance
15769 * Currently all parent columns must be found in child. Missing columns are an
15770 * error. One day we might consider creating new columns like CREATE TABLE
15771 * does. However, that is widely unpopular --- in the common use case of
15772 * partitioned tables it's a foot-gun.
15774 * The data type must match exactly. If the parent column is NOT NULL then
15775 * the child must be as well. Defaults are not compared, however.
15777 static void
15778 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
15780 Relation attrrel;
15781 TupleDesc parent_desc;
15783 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
15784 parent_desc = RelationGetDescr(parent_rel);
15786 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
15788 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
15789 char *parent_attname = NameStr(parent_att->attname);
15790 HeapTuple tuple;
15792 /* Ignore dropped columns in the parent. */
15793 if (parent_att->attisdropped)
15794 continue;
15796 /* Find same column in child (matching on column name). */
15797 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
15798 if (HeapTupleIsValid(tuple))
15800 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
15802 if (parent_att->atttypid != child_att->atttypid ||
15803 parent_att->atttypmod != child_att->atttypmod)
15804 ereport(ERROR,
15805 (errcode(ERRCODE_DATATYPE_MISMATCH),
15806 errmsg("child table \"%s\" has different type for column \"%s\"",
15807 RelationGetRelationName(child_rel), parent_attname)));
15809 if (parent_att->attcollation != child_att->attcollation)
15810 ereport(ERROR,
15811 (errcode(ERRCODE_COLLATION_MISMATCH),
15812 errmsg("child table \"%s\" has different collation for column \"%s\"",
15813 RelationGetRelationName(child_rel), parent_attname)));
15816 * If the parent has a not-null constraint that's not NO INHERIT,
15817 * make sure the child has one too.
15819 * Other constraints are checked elsewhere.
15821 if (parent_att->attnotnull && !child_att->attnotnull)
15823 HeapTuple contup;
15825 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
15826 parent_att->attnum);
15827 if (HeapTupleIsValid(contup) &&
15828 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
15829 ereport(ERROR,
15830 errcode(ERRCODE_DATATYPE_MISMATCH),
15831 errmsg("column \"%s\" in child table must be marked NOT NULL",
15832 parent_attname));
15836 * Child column must be generated if and only if parent column is.
15838 if (parent_att->attgenerated && !child_att->attgenerated)
15839 ereport(ERROR,
15840 (errcode(ERRCODE_DATATYPE_MISMATCH),
15841 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
15842 if (child_att->attgenerated && !parent_att->attgenerated)
15843 ereport(ERROR,
15844 (errcode(ERRCODE_DATATYPE_MISMATCH),
15845 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
15848 * OK, bump the child column's inheritance count. (If we fail
15849 * later on, this change will just roll back.)
15851 child_att->attinhcount++;
15852 if (child_att->attinhcount < 0)
15853 ereport(ERROR,
15854 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15855 errmsg("too many inheritance parents"));
15858 * In case of partitions, we must enforce that value of attislocal
15859 * is same in all partitions. (Note: there are only inherited
15860 * attributes in partitions)
15862 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15864 Assert(child_att->attinhcount == 1);
15865 child_att->attislocal = false;
15868 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
15869 heap_freetuple(tuple);
15871 else
15873 ereport(ERROR,
15874 (errcode(ERRCODE_DATATYPE_MISMATCH),
15875 errmsg("child table is missing column \"%s\"", parent_attname)));
15879 table_close(attrrel, RowExclusiveLock);
15883 * Check constraints in child table match up with constraints in parent,
15884 * and increment their coninhcount.
15886 * Constraints that are marked ONLY in the parent are ignored.
15888 * Called by CreateInheritance
15890 * Currently all constraints in parent must be present in the child. One day we
15891 * may consider adding new constraints like CREATE TABLE does.
15893 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15894 * constraints. As long as tables have more like 10 constraints it shouldn't be
15895 * a problem though. Even 100 constraints ought not be the end of the world.
15897 * XXX See MergeWithExistingConstraint too if you change this code.
15899 static void
15900 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
15902 Relation constraintrel;
15903 SysScanDesc parent_scan;
15904 ScanKeyData parent_key;
15905 HeapTuple parent_tuple;
15906 Oid parent_relid = RelationGetRelid(parent_rel);
15908 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
15910 /* Outer loop scans through the parent's constraint definitions */
15911 ScanKeyInit(&parent_key,
15912 Anum_pg_constraint_conrelid,
15913 BTEqualStrategyNumber, F_OIDEQ,
15914 ObjectIdGetDatum(parent_relid));
15915 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15916 true, NULL, 1, &parent_key);
15918 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15920 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15921 SysScanDesc child_scan;
15922 ScanKeyData child_key;
15923 HeapTuple child_tuple;
15924 bool found = false;
15926 if (parent_con->contype != CONSTRAINT_CHECK &&
15927 parent_con->contype != CONSTRAINT_NOTNULL)
15928 continue;
15930 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15931 if (parent_con->connoinherit)
15932 continue;
15934 /* Search for a child constraint matching this one */
15935 ScanKeyInit(&child_key,
15936 Anum_pg_constraint_conrelid,
15937 BTEqualStrategyNumber, F_OIDEQ,
15938 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15939 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15940 true, NULL, 1, &child_key);
15942 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15944 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
15945 HeapTuple child_copy;
15947 if (child_con->contype != parent_con->contype)
15948 continue;
15951 * CHECK constraint are matched by name, NOT NULL ones by
15952 * attribute number
15954 if (child_con->contype == CONSTRAINT_CHECK)
15956 if (strcmp(NameStr(parent_con->conname),
15957 NameStr(child_con->conname)) != 0)
15958 continue;
15960 else if (child_con->contype == CONSTRAINT_NOTNULL)
15962 AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
15963 AttrNumber child_attno = extractNotNullColumn(child_tuple);
15965 if (strcmp(get_attname(parent_relid, parent_attno, false),
15966 get_attname(RelationGetRelid(child_rel), child_attno,
15967 false)) != 0)
15968 continue;
15971 if (child_con->contype == CONSTRAINT_CHECK &&
15972 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
15973 ereport(ERROR,
15974 (errcode(ERRCODE_DATATYPE_MISMATCH),
15975 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15976 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
15979 * If the CHECK child constraint is "no inherit" then cannot
15980 * merge.
15982 * This is not desirable for not-null constraints, mostly because
15983 * it breaks our pg_upgrade strategy, but it also makes sense on
15984 * its own: if a child has its own not-null constraint and then
15985 * acquires a parent with the same constraint, then we start to
15986 * enforce that constraint for all the descendants of that child
15987 * too, if any.
15989 if (child_con->contype == CONSTRAINT_CHECK &&
15990 child_con->connoinherit)
15991 ereport(ERROR,
15992 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15993 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15994 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15997 * If the child constraint is "not valid" then cannot merge with a
15998 * valid parent constraint
16000 if (parent_con->convalidated && !child_con->convalidated)
16001 ereport(ERROR,
16002 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16003 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16004 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16007 * OK, bump the child constraint's inheritance count. (If we fail
16008 * later on, this change will just roll back.)
16010 child_copy = heap_copytuple(child_tuple);
16011 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16012 child_con->coninhcount++;
16013 if (child_con->coninhcount < 0)
16014 ereport(ERROR,
16015 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16016 errmsg("too many inheritance parents"));
16017 if (child_con->contype == CONSTRAINT_NOTNULL &&
16018 child_con->connoinherit)
16019 child_con->connoinherit = false;
16022 * In case of partitions, an inherited constraint must be
16023 * inherited only once since it cannot have multiple parents and
16024 * it is never considered local.
16026 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16028 Assert(child_con->coninhcount == 1);
16029 child_con->conislocal = false;
16032 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16033 heap_freetuple(child_copy);
16035 found = true;
16036 break;
16039 systable_endscan(child_scan);
16041 if (!found)
16043 if (parent_con->contype == CONSTRAINT_NOTNULL)
16044 ereport(ERROR,
16045 errcode(ERRCODE_DATATYPE_MISMATCH),
16046 errmsg("column \"%s\" in child table must be marked NOT NULL",
16047 get_attname(parent_relid,
16048 extractNotNullColumn(parent_tuple),
16049 false)));
16051 ereport(ERROR,
16052 (errcode(ERRCODE_DATATYPE_MISMATCH),
16053 errmsg("child table is missing constraint \"%s\"",
16054 NameStr(parent_con->conname))));
16058 systable_endscan(parent_scan);
16059 table_close(constraintrel, RowExclusiveLock);
16063 * ALTER TABLE NO INHERIT
16065 * Return value is the address of the relation that is no longer parent.
16067 static ObjectAddress
16068 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16070 ObjectAddress address;
16071 Relation parent_rel;
16073 if (rel->rd_rel->relispartition)
16074 ereport(ERROR,
16075 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16076 errmsg("cannot change inheritance of a partition")));
16079 * AccessShareLock on the parent is probably enough, seeing that DROP
16080 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16081 * be inspecting the parent's schema.
16083 parent_rel = table_openrv(parent, AccessShareLock);
16086 * We don't bother to check ownership of the parent table --- ownership of
16087 * the child is presumed enough rights.
16090 /* Off to RemoveInheritance() where most of the work happens */
16091 RemoveInheritance(rel, parent_rel, false);
16094 * If parent_rel has a primary key, then child_rel has not-null
16095 * constraints that make these columns as non nullable. Mark those
16096 * constraints as no longer inherited by this parent.
16098 ATInheritAdjustNotNulls(parent_rel, rel, -1);
16101 * If the parent has a primary key, then we decrement counts for all NOT
16102 * NULL constraints
16105 ObjectAddressSet(address, RelationRelationId,
16106 RelationGetRelid(parent_rel));
16108 /* keep our lock on the parent relation until commit */
16109 table_close(parent_rel, NoLock);
16111 return address;
16115 * MarkInheritDetached
16117 * Set inhdetachpending for a partition, for ATExecDetachPartition
16118 * in concurrent mode. While at it, verify that no other partition is
16119 * already pending detach.
16121 static void
16122 MarkInheritDetached(Relation child_rel, Relation parent_rel)
16124 Relation catalogRelation;
16125 SysScanDesc scan;
16126 ScanKeyData key;
16127 HeapTuple inheritsTuple;
16128 bool found = false;
16130 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16133 * Find pg_inherits entries by inhparent. (We need to scan them all in
16134 * order to verify that no other partition is pending detach.)
16136 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16137 ScanKeyInit(&key,
16138 Anum_pg_inherits_inhparent,
16139 BTEqualStrategyNumber, F_OIDEQ,
16140 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16141 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16142 true, NULL, 1, &key);
16144 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16146 Form_pg_inherits inhForm;
16148 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16149 if (inhForm->inhdetachpending)
16150 ereport(ERROR,
16151 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16152 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16153 get_rel_name(inhForm->inhrelid),
16154 get_namespace_name(parent_rel->rd_rel->relnamespace),
16155 RelationGetRelationName(parent_rel)),
16156 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16158 if (inhForm->inhrelid == RelationGetRelid(child_rel))
16160 HeapTuple newtup;
16162 newtup = heap_copytuple(inheritsTuple);
16163 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16165 CatalogTupleUpdate(catalogRelation,
16166 &inheritsTuple->t_self,
16167 newtup);
16168 found = true;
16169 heap_freetuple(newtup);
16170 /* keep looking, to ensure we catch others pending detach */
16174 /* Done */
16175 systable_endscan(scan);
16176 table_close(catalogRelation, RowExclusiveLock);
16178 if (!found)
16179 ereport(ERROR,
16180 (errcode(ERRCODE_UNDEFINED_TABLE),
16181 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16182 RelationGetRelationName(child_rel),
16183 RelationGetRelationName(parent_rel))));
16187 * RemoveInheritance
16189 * Drop a parent from the child's parents. This just adjusts the attinhcount
16190 * and attislocal of the columns and removes the pg_inherit and pg_depend
16191 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16193 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16194 * up attislocal stays true, which means if a child is ever removed from a
16195 * parent then its columns will never be automatically dropped which may
16196 * surprise. But at least we'll never surprise by dropping columns someone
16197 * isn't expecting to be dropped which would actually mean data loss.
16199 * coninhcount and conislocal for inherited constraints are adjusted in
16200 * exactly the same way.
16202 * Common to ATExecDropInherit() and ATExecDetachPartition().
16204 static void
16205 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16207 Relation catalogRelation;
16208 SysScanDesc scan;
16209 ScanKeyData key[3];
16210 HeapTuple attributeTuple,
16211 constraintTuple;
16212 List *connames;
16213 List *nncolumns;
16214 bool found;
16215 bool is_partitioning;
16217 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16219 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16220 RelationGetRelid(parent_rel),
16221 expect_detached,
16222 RelationGetRelationName(child_rel));
16223 if (!found)
16225 if (is_partitioning)
16226 ereport(ERROR,
16227 (errcode(ERRCODE_UNDEFINED_TABLE),
16228 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16229 RelationGetRelationName(child_rel),
16230 RelationGetRelationName(parent_rel))));
16231 else
16232 ereport(ERROR,
16233 (errcode(ERRCODE_UNDEFINED_TABLE),
16234 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16235 RelationGetRelationName(parent_rel),
16236 RelationGetRelationName(child_rel))));
16240 * Search through child columns looking for ones matching parent rel
16242 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16243 ScanKeyInit(&key[0],
16244 Anum_pg_attribute_attrelid,
16245 BTEqualStrategyNumber, F_OIDEQ,
16246 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16247 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16248 true, NULL, 1, key);
16249 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16251 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16253 /* Ignore if dropped or not inherited */
16254 if (att->attisdropped)
16255 continue;
16256 if (att->attinhcount <= 0)
16257 continue;
16259 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16260 NameStr(att->attname)))
16262 /* Decrement inhcount and possibly set islocal to true */
16263 HeapTuple copyTuple = heap_copytuple(attributeTuple);
16264 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16266 copy_att->attinhcount--;
16267 if (copy_att->attinhcount == 0)
16268 copy_att->attislocal = true;
16270 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16271 heap_freetuple(copyTuple);
16274 systable_endscan(scan);
16275 table_close(catalogRelation, RowExclusiveLock);
16278 * Likewise, find inherited check constraints and disinherit them. To do
16279 * this, we first need a list of the names of the parent's check
16280 * constraints. (We cheat a bit by only checking for name matches,
16281 * assuming that the expressions will match.)
16283 * For NOT NULL columns, we store column numbers to match.
16285 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16286 ScanKeyInit(&key[0],
16287 Anum_pg_constraint_conrelid,
16288 BTEqualStrategyNumber, F_OIDEQ,
16289 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16290 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16291 true, NULL, 1, key);
16293 connames = NIL;
16294 nncolumns = NIL;
16296 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16298 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16300 if (con->contype == CONSTRAINT_CHECK)
16301 connames = lappend(connames, pstrdup(NameStr(con->conname)));
16302 if (con->contype == CONSTRAINT_NOTNULL)
16303 nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
16306 systable_endscan(scan);
16308 /* Now scan the child's constraints */
16309 ScanKeyInit(&key[0],
16310 Anum_pg_constraint_conrelid,
16311 BTEqualStrategyNumber, F_OIDEQ,
16312 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16313 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16314 true, NULL, 1, key);
16316 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16318 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16319 bool match = false;
16320 ListCell *lc;
16323 * Match CHECK constraints by name, not-null constraints by column
16324 * number, and ignore all others.
16326 if (con->contype == CONSTRAINT_CHECK)
16328 foreach(lc, connames)
16330 if (con->contype == CONSTRAINT_CHECK &&
16331 strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
16333 match = true;
16334 break;
16338 else if (con->contype == CONSTRAINT_NOTNULL)
16340 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16342 foreach(lc, nncolumns)
16344 if (lfirst_int(lc) == child_attno)
16346 match = true;
16347 break;
16351 else
16352 continue;
16354 if (match)
16356 /* Decrement inhcount and possibly set islocal to true */
16357 HeapTuple copyTuple = heap_copytuple(constraintTuple);
16358 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16360 if (copy_con->coninhcount <= 0) /* shouldn't happen */
16361 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16362 RelationGetRelid(child_rel), NameStr(copy_con->conname));
16364 copy_con->coninhcount--;
16365 if (copy_con->coninhcount == 0)
16366 copy_con->conislocal = true;
16368 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16369 heap_freetuple(copyTuple);
16373 systable_endscan(scan);
16374 table_close(catalogRelation, RowExclusiveLock);
16376 drop_parent_dependency(RelationGetRelid(child_rel),
16377 RelationRelationId,
16378 RelationGetRelid(parent_rel),
16379 child_dependency_type(is_partitioning));
16382 * Post alter hook of this inherits. Since object_access_hook doesn't take
16383 * multiple object identifiers, we relay oid of parent relation using
16384 * auxiliary_id argument.
16386 InvokeObjectPostAlterHookArg(InheritsRelationId,
16387 RelationGetRelid(child_rel), 0,
16388 RelationGetRelid(parent_rel), false);
16392 * Adjust coninhcount of not-null constraints upwards or downwards when a
16393 * table is marked as inheriting or no longer doing so a table with a primary
16394 * key.
16396 * Note: these constraints are not dropped, even if their inhcount goes to zero
16397 * and conislocal is false. Instead we mark the constraints as locally defined.
16398 * This is seen as more useful behavior, with no downsides. The user can always
16399 * drop them afterwards.
16401 static void
16402 ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
16404 Bitmapset *pkattnos;
16406 /* Quick exit when parent has no PK */
16407 if (!parent_rel->rd_rel->relhasindex)
16408 return;
16410 pkattnos = RelationGetIndexAttrBitmap(parent_rel,
16411 INDEX_ATTR_BITMAP_PRIMARY_KEY);
16412 if (pkattnos != NULL)
16414 Bitmapset *childattnums = NULL;
16415 AttrMap *attmap;
16416 int i;
16418 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16419 RelationGetDescr(child_rel), true);
16421 i = -1;
16422 while ((i = bms_next_member(pkattnos, i)) >= 0)
16424 childattnums = bms_add_member(childattnums,
16425 attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
16429 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
16430 * parent: the relevant not-null constraint in the child already had
16431 * its inhcount modified earlier.
16433 CommandCounterIncrement();
16434 AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
16435 inhcount);
16440 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16441 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16442 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16443 * be TypeRelationId). There's no convenient way to do this, so go trawling
16444 * through pg_depend.
16446 static void
16447 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16448 DependencyType deptype)
16450 Relation catalogRelation;
16451 SysScanDesc scan;
16452 ScanKeyData key[3];
16453 HeapTuple depTuple;
16455 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16457 ScanKeyInit(&key[0],
16458 Anum_pg_depend_classid,
16459 BTEqualStrategyNumber, F_OIDEQ,
16460 ObjectIdGetDatum(RelationRelationId));
16461 ScanKeyInit(&key[1],
16462 Anum_pg_depend_objid,
16463 BTEqualStrategyNumber, F_OIDEQ,
16464 ObjectIdGetDatum(relid));
16465 ScanKeyInit(&key[2],
16466 Anum_pg_depend_objsubid,
16467 BTEqualStrategyNumber, F_INT4EQ,
16468 Int32GetDatum(0));
16470 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16471 NULL, 3, key);
16473 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16475 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16477 if (dep->refclassid == refclassid &&
16478 dep->refobjid == refobjid &&
16479 dep->refobjsubid == 0 &&
16480 dep->deptype == deptype)
16481 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
16484 systable_endscan(scan);
16485 table_close(catalogRelation, RowExclusiveLock);
16489 * ALTER TABLE OF
16491 * Attach a table to a composite type, as though it had been created with CREATE
16492 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16493 * subject table must not have inheritance parents. These restrictions ensure
16494 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16496 * The address of the type is returned.
16498 static ObjectAddress
16499 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
16501 Oid relid = RelationGetRelid(rel);
16502 Type typetuple;
16503 Form_pg_type typeform;
16504 Oid typeid;
16505 Relation inheritsRelation,
16506 relationRelation;
16507 SysScanDesc scan;
16508 ScanKeyData key;
16509 AttrNumber table_attno,
16510 type_attno;
16511 TupleDesc typeTupleDesc,
16512 tableTupleDesc;
16513 ObjectAddress tableobj,
16514 typeobj;
16515 HeapTuple classtuple;
16517 /* Validate the type. */
16518 typetuple = typenameType(NULL, ofTypename, NULL);
16519 check_of_type(typetuple);
16520 typeform = (Form_pg_type) GETSTRUCT(typetuple);
16521 typeid = typeform->oid;
16523 /* Fail if the table has any inheritance parents. */
16524 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
16525 ScanKeyInit(&key,
16526 Anum_pg_inherits_inhrelid,
16527 BTEqualStrategyNumber, F_OIDEQ,
16528 ObjectIdGetDatum(relid));
16529 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
16530 true, NULL, 1, &key);
16531 if (HeapTupleIsValid(systable_getnext(scan)))
16532 ereport(ERROR,
16533 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16534 errmsg("typed tables cannot inherit")));
16535 systable_endscan(scan);
16536 table_close(inheritsRelation, AccessShareLock);
16539 * Check the tuple descriptors for compatibility. Unlike inheritance, we
16540 * require that the order also match. However, attnotnull need not match.
16542 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
16543 tableTupleDesc = RelationGetDescr(rel);
16544 table_attno = 1;
16545 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
16547 Form_pg_attribute type_attr,
16548 table_attr;
16549 const char *type_attname,
16550 *table_attname;
16552 /* Get the next non-dropped type attribute. */
16553 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
16554 if (type_attr->attisdropped)
16555 continue;
16556 type_attname = NameStr(type_attr->attname);
16558 /* Get the next non-dropped table attribute. */
16561 if (table_attno > tableTupleDesc->natts)
16562 ereport(ERROR,
16563 (errcode(ERRCODE_DATATYPE_MISMATCH),
16564 errmsg("table is missing column \"%s\"",
16565 type_attname)));
16566 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
16567 table_attno++;
16568 } while (table_attr->attisdropped);
16569 table_attname = NameStr(table_attr->attname);
16571 /* Compare name. */
16572 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
16573 ereport(ERROR,
16574 (errcode(ERRCODE_DATATYPE_MISMATCH),
16575 errmsg("table has column \"%s\" where type requires \"%s\"",
16576 table_attname, type_attname)));
16578 /* Compare type. */
16579 if (table_attr->atttypid != type_attr->atttypid ||
16580 table_attr->atttypmod != type_attr->atttypmod ||
16581 table_attr->attcollation != type_attr->attcollation)
16582 ereport(ERROR,
16583 (errcode(ERRCODE_DATATYPE_MISMATCH),
16584 errmsg("table \"%s\" has different type for column \"%s\"",
16585 RelationGetRelationName(rel), type_attname)));
16587 ReleaseTupleDesc(typeTupleDesc);
16589 /* Any remaining columns at the end of the table had better be dropped. */
16590 for (; table_attno <= tableTupleDesc->natts; table_attno++)
16592 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
16593 table_attno - 1);
16595 if (!table_attr->attisdropped)
16596 ereport(ERROR,
16597 (errcode(ERRCODE_DATATYPE_MISMATCH),
16598 errmsg("table has extra column \"%s\"",
16599 NameStr(table_attr->attname))));
16602 /* If the table was already typed, drop the existing dependency. */
16603 if (rel->rd_rel->reloftype)
16604 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16605 DEPENDENCY_NORMAL);
16607 /* Record a dependency on the new type. */
16608 tableobj.classId = RelationRelationId;
16609 tableobj.objectId = relid;
16610 tableobj.objectSubId = 0;
16611 typeobj.classId = TypeRelationId;
16612 typeobj.objectId = typeid;
16613 typeobj.objectSubId = 0;
16614 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
16616 /* Update pg_class.reloftype */
16617 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16618 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16619 if (!HeapTupleIsValid(classtuple))
16620 elog(ERROR, "cache lookup failed for relation %u", relid);
16621 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
16622 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
16624 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16626 heap_freetuple(classtuple);
16627 table_close(relationRelation, RowExclusiveLock);
16629 ReleaseSysCache(typetuple);
16631 return typeobj;
16635 * ALTER TABLE NOT OF
16637 * Detach a typed table from its originating type. Just clear reloftype and
16638 * remove the dependency.
16640 static void
16641 ATExecDropOf(Relation rel, LOCKMODE lockmode)
16643 Oid relid = RelationGetRelid(rel);
16644 Relation relationRelation;
16645 HeapTuple tuple;
16647 if (!OidIsValid(rel->rd_rel->reloftype))
16648 ereport(ERROR,
16649 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16650 errmsg("\"%s\" is not a typed table",
16651 RelationGetRelationName(rel))));
16654 * We don't bother to check ownership of the type --- ownership of the
16655 * table is presumed enough rights. No lock required on the type, either.
16658 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16659 DEPENDENCY_NORMAL);
16661 /* Clear pg_class.reloftype */
16662 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16663 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16664 if (!HeapTupleIsValid(tuple))
16665 elog(ERROR, "cache lookup failed for relation %u", relid);
16666 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
16667 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
16669 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16671 heap_freetuple(tuple);
16672 table_close(relationRelation, RowExclusiveLock);
16676 * relation_mark_replica_identity: Update a table's replica identity
16678 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16679 * index. Otherwise, it must be InvalidOid.
16681 * Caller had better hold an exclusive lock on the relation, as the results
16682 * of running two of these concurrently wouldn't be pretty.
16684 static void
16685 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
16686 bool is_internal)
16688 Relation pg_index;
16689 Relation pg_class;
16690 HeapTuple pg_class_tuple;
16691 HeapTuple pg_index_tuple;
16692 Form_pg_class pg_class_form;
16693 Form_pg_index pg_index_form;
16694 ListCell *index;
16697 * Check whether relreplident has changed, and update it if so.
16699 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16700 pg_class_tuple = SearchSysCacheCopy1(RELOID,
16701 ObjectIdGetDatum(RelationGetRelid(rel)));
16702 if (!HeapTupleIsValid(pg_class_tuple))
16703 elog(ERROR, "cache lookup failed for relation \"%s\"",
16704 RelationGetRelationName(rel));
16705 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
16706 if (pg_class_form->relreplident != ri_type)
16708 pg_class_form->relreplident = ri_type;
16709 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
16711 table_close(pg_class, RowExclusiveLock);
16712 heap_freetuple(pg_class_tuple);
16715 * Update the per-index indisreplident flags correctly.
16717 pg_index = table_open(IndexRelationId, RowExclusiveLock);
16718 foreach(index, RelationGetIndexList(rel))
16720 Oid thisIndexOid = lfirst_oid(index);
16721 bool dirty = false;
16723 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
16724 ObjectIdGetDatum(thisIndexOid));
16725 if (!HeapTupleIsValid(pg_index_tuple))
16726 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
16727 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
16729 if (thisIndexOid == indexOid)
16731 /* Set the bit if not already set. */
16732 if (!pg_index_form->indisreplident)
16734 dirty = true;
16735 pg_index_form->indisreplident = true;
16738 else
16740 /* Unset the bit if set. */
16741 if (pg_index_form->indisreplident)
16743 dirty = true;
16744 pg_index_form->indisreplident = false;
16748 if (dirty)
16750 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
16751 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
16752 InvalidOid, is_internal);
16755 * Invalidate the relcache for the table, so that after we commit
16756 * all sessions will refresh the table's replica identity index
16757 * before attempting any UPDATE or DELETE on the table. (If we
16758 * changed the table's pg_class row above, then a relcache inval
16759 * is already queued due to that; but we might not have.)
16761 CacheInvalidateRelcache(rel);
16763 heap_freetuple(pg_index_tuple);
16766 table_close(pg_index, RowExclusiveLock);
16770 * ALTER TABLE <name> REPLICA IDENTITY ...
16772 static void
16773 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
16775 Oid indexOid;
16776 Relation indexRel;
16777 int key;
16779 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16781 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16782 return;
16784 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16786 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16787 return;
16789 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16791 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16792 return;
16794 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
16796 /* fallthrough */ ;
16798 else
16799 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16801 /* Check that the index exists */
16802 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16803 if (!OidIsValid(indexOid))
16804 ereport(ERROR,
16805 (errcode(ERRCODE_UNDEFINED_OBJECT),
16806 errmsg("index \"%s\" for table \"%s\" does not exist",
16807 stmt->name, RelationGetRelationName(rel))));
16809 indexRel = index_open(indexOid, ShareLock);
16811 /* Check that the index is on the relation we're altering. */
16812 if (indexRel->rd_index == NULL ||
16813 indexRel->rd_index->indrelid != RelationGetRelid(rel))
16814 ereport(ERROR,
16815 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16816 errmsg("\"%s\" is not an index for table \"%s\"",
16817 RelationGetRelationName(indexRel),
16818 RelationGetRelationName(rel))));
16819 /* The AM must support uniqueness, and the index must in fact be unique. */
16820 if (!indexRel->rd_indam->amcanunique ||
16821 !indexRel->rd_index->indisunique)
16822 ereport(ERROR,
16823 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16824 errmsg("cannot use non-unique index \"%s\" as replica identity",
16825 RelationGetRelationName(indexRel))));
16826 /* Deferred indexes are not guaranteed to be always unique. */
16827 if (!indexRel->rd_index->indimmediate)
16828 ereport(ERROR,
16829 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16830 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16831 RelationGetRelationName(indexRel))));
16832 /* Expression indexes aren't supported. */
16833 if (RelationGetIndexExpressions(indexRel) != NIL)
16834 ereport(ERROR,
16835 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16836 errmsg("cannot use expression index \"%s\" as replica identity",
16837 RelationGetRelationName(indexRel))));
16838 /* Predicate indexes aren't supported. */
16839 if (RelationGetIndexPredicate(indexRel) != NIL)
16840 ereport(ERROR,
16841 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16842 errmsg("cannot use partial index \"%s\" as replica identity",
16843 RelationGetRelationName(indexRel))));
16845 /* Check index for nullable columns. */
16846 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16848 int16 attno = indexRel->rd_index->indkey.values[key];
16849 Form_pg_attribute attr;
16852 * Reject any other system columns. (Going forward, we'll disallow
16853 * indexes containing such columns in the first place, but they might
16854 * exist in older branches.)
16856 if (attno <= 0)
16857 ereport(ERROR,
16858 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16859 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16860 RelationGetRelationName(indexRel), attno)));
16862 attr = TupleDescAttr(rel->rd_att, attno - 1);
16863 if (!attr->attnotnull)
16864 ereport(ERROR,
16865 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16866 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16867 RelationGetRelationName(indexRel),
16868 NameStr(attr->attname))));
16871 /* This index is suitable for use as a replica identity. Mark it. */
16872 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16874 index_close(indexRel, NoLock);
16878 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16880 static void
16881 ATExecSetRowSecurity(Relation rel, bool rls)
16883 Relation pg_class;
16884 Oid relid;
16885 HeapTuple tuple;
16887 relid = RelationGetRelid(rel);
16889 /* Pull the record for this relation and update it */
16890 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16892 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16894 if (!HeapTupleIsValid(tuple))
16895 elog(ERROR, "cache lookup failed for relation %u", relid);
16897 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
16898 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16900 InvokeObjectPostAlterHook(RelationRelationId,
16901 RelationGetRelid(rel), 0);
16903 table_close(pg_class, RowExclusiveLock);
16904 heap_freetuple(tuple);
16908 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16910 static void
16911 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
16913 Relation pg_class;
16914 Oid relid;
16915 HeapTuple tuple;
16917 relid = RelationGetRelid(rel);
16919 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16921 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16923 if (!HeapTupleIsValid(tuple))
16924 elog(ERROR, "cache lookup failed for relation %u", relid);
16926 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
16927 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16929 InvokeObjectPostAlterHook(RelationRelationId,
16930 RelationGetRelid(rel), 0);
16932 table_close(pg_class, RowExclusiveLock);
16933 heap_freetuple(tuple);
16937 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16939 static void
16940 ATExecGenericOptions(Relation rel, List *options)
16942 Relation ftrel;
16943 ForeignServer *server;
16944 ForeignDataWrapper *fdw;
16945 HeapTuple tuple;
16946 bool isnull;
16947 Datum repl_val[Natts_pg_foreign_table];
16948 bool repl_null[Natts_pg_foreign_table];
16949 bool repl_repl[Natts_pg_foreign_table];
16950 Datum datum;
16951 Form_pg_foreign_table tableform;
16953 if (options == NIL)
16954 return;
16956 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
16958 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
16959 ObjectIdGetDatum(rel->rd_id));
16960 if (!HeapTupleIsValid(tuple))
16961 ereport(ERROR,
16962 (errcode(ERRCODE_UNDEFINED_OBJECT),
16963 errmsg("foreign table \"%s\" does not exist",
16964 RelationGetRelationName(rel))));
16965 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16966 server = GetForeignServer(tableform->ftserver);
16967 fdw = GetForeignDataWrapper(server->fdwid);
16969 memset(repl_val, 0, sizeof(repl_val));
16970 memset(repl_null, false, sizeof(repl_null));
16971 memset(repl_repl, false, sizeof(repl_repl));
16973 /* Extract the current options */
16974 datum = SysCacheGetAttr(FOREIGNTABLEREL,
16975 tuple,
16976 Anum_pg_foreign_table_ftoptions,
16977 &isnull);
16978 if (isnull)
16979 datum = PointerGetDatum(NULL);
16981 /* Transform the options */
16982 datum = transformGenericOptions(ForeignTableRelationId,
16983 datum,
16984 options,
16985 fdw->fdwvalidator);
16987 if (PointerIsValid(DatumGetPointer(datum)))
16988 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
16989 else
16990 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16992 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16994 /* Everything looks good - update the tuple */
16996 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16997 repl_val, repl_null, repl_repl);
16999 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17002 * Invalidate relcache so that all sessions will refresh any cached plans
17003 * that might depend on the old options.
17005 CacheInvalidateRelcache(rel);
17007 InvokeObjectPostAlterHook(ForeignTableRelationId,
17008 RelationGetRelid(rel), 0);
17010 table_close(ftrel, RowExclusiveLock);
17012 heap_freetuple(tuple);
17016 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17018 * Return value is the address of the modified column
17020 static ObjectAddress
17021 ATExecSetCompression(Relation rel,
17022 const char *column,
17023 Node *newValue,
17024 LOCKMODE lockmode)
17026 Relation attrel;
17027 HeapTuple tuple;
17028 Form_pg_attribute atttableform;
17029 AttrNumber attnum;
17030 char *compression;
17031 char cmethod;
17032 ObjectAddress address;
17034 compression = strVal(newValue);
17036 attrel = table_open(AttributeRelationId, RowExclusiveLock);
17038 /* copy the cache entry so we can scribble on it below */
17039 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17040 if (!HeapTupleIsValid(tuple))
17041 ereport(ERROR,
17042 (errcode(ERRCODE_UNDEFINED_COLUMN),
17043 errmsg("column \"%s\" of relation \"%s\" does not exist",
17044 column, RelationGetRelationName(rel))));
17046 /* prevent them from altering a system attribute */
17047 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17048 attnum = atttableform->attnum;
17049 if (attnum <= 0)
17050 ereport(ERROR,
17051 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17052 errmsg("cannot alter system column \"%s\"", column)));
17055 * Check that column type is compressible, then get the attribute
17056 * compression method code
17058 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17060 /* update pg_attribute entry */
17061 atttableform->attcompression = cmethod;
17062 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17064 InvokeObjectPostAlterHook(RelationRelationId,
17065 RelationGetRelid(rel),
17066 attnum);
17069 * Apply the change to indexes as well (only for simple index columns,
17070 * matching behavior of index.c ConstructTupleDescriptor()).
17072 SetIndexStorageProperties(rel, attrel, attnum,
17073 false, 0,
17074 true, cmethod,
17075 lockmode);
17077 heap_freetuple(tuple);
17079 table_close(attrel, RowExclusiveLock);
17081 /* make changes visible */
17082 CommandCounterIncrement();
17084 ObjectAddressSubSet(address, RelationRelationId,
17085 RelationGetRelid(rel), attnum);
17086 return address;
17091 * Preparation phase for SET LOGGED/UNLOGGED
17093 * This verifies that we're not trying to change a temp table. Also,
17094 * existing foreign key constraints are checked to avoid ending up with
17095 * permanent tables referencing unlogged tables.
17097 * Return value is false if the operation is a no-op (in which case the
17098 * checks are skipped), otherwise true.
17100 static bool
17101 ATPrepChangePersistence(Relation rel, bool toLogged)
17103 Relation pg_constraint;
17104 HeapTuple tuple;
17105 SysScanDesc scan;
17106 ScanKeyData skey[1];
17109 * Disallow changing status for a temp table. Also verify whether we can
17110 * get away with doing nothing; in such cases we don't need to run the
17111 * checks below, either.
17113 switch (rel->rd_rel->relpersistence)
17115 case RELPERSISTENCE_TEMP:
17116 ereport(ERROR,
17117 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17118 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17119 RelationGetRelationName(rel)),
17120 errtable(rel)));
17121 break;
17122 case RELPERSISTENCE_PERMANENT:
17123 if (toLogged)
17124 /* nothing to do */
17125 return false;
17126 break;
17127 case RELPERSISTENCE_UNLOGGED:
17128 if (!toLogged)
17129 /* nothing to do */
17130 return false;
17131 break;
17135 * Check that the table is not part of any publication when changing to
17136 * UNLOGGED, as UNLOGGED tables can't be published.
17138 if (!toLogged &&
17139 GetRelationPublications(RelationGetRelid(rel)) != NIL)
17140 ereport(ERROR,
17141 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17142 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17143 RelationGetRelationName(rel)),
17144 errdetail("Unlogged relations cannot be replicated.")));
17147 * Check existing foreign key constraints to preserve the invariant that
17148 * permanent tables cannot reference unlogged ones. Self-referencing
17149 * foreign keys can safely be ignored.
17151 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17154 * Scan conrelid if changing to permanent, else confrelid. This also
17155 * determines whether a useful index exists.
17157 ScanKeyInit(&skey[0],
17158 toLogged ? Anum_pg_constraint_conrelid :
17159 Anum_pg_constraint_confrelid,
17160 BTEqualStrategyNumber, F_OIDEQ,
17161 ObjectIdGetDatum(RelationGetRelid(rel)));
17162 scan = systable_beginscan(pg_constraint,
17163 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17164 true, NULL, 1, skey);
17166 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17168 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17170 if (con->contype == CONSTRAINT_FOREIGN)
17172 Oid foreignrelid;
17173 Relation foreignrel;
17175 /* the opposite end of what we used as scankey */
17176 foreignrelid = toLogged ? con->confrelid : con->conrelid;
17178 /* ignore if self-referencing */
17179 if (RelationGetRelid(rel) == foreignrelid)
17180 continue;
17182 foreignrel = relation_open(foreignrelid, AccessShareLock);
17184 if (toLogged)
17186 if (!RelationIsPermanent(foreignrel))
17187 ereport(ERROR,
17188 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17189 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17190 RelationGetRelationName(rel),
17191 RelationGetRelationName(foreignrel)),
17192 errtableconstraint(rel, NameStr(con->conname))));
17194 else
17196 if (RelationIsPermanent(foreignrel))
17197 ereport(ERROR,
17198 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17199 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17200 RelationGetRelationName(rel),
17201 RelationGetRelationName(foreignrel)),
17202 errtableconstraint(rel, NameStr(con->conname))));
17205 relation_close(foreignrel, AccessShareLock);
17209 systable_endscan(scan);
17211 table_close(pg_constraint, AccessShareLock);
17213 return true;
17217 * Execute ALTER TABLE SET SCHEMA
17219 ObjectAddress
17220 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17222 Relation rel;
17223 Oid relid;
17224 Oid oldNspOid;
17225 Oid nspOid;
17226 RangeVar *newrv;
17227 ObjectAddresses *objsMoved;
17228 ObjectAddress myself;
17230 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17231 stmt->missing_ok ? RVR_MISSING_OK : 0,
17232 RangeVarCallbackForAlterRelation,
17233 (void *) stmt);
17235 if (!OidIsValid(relid))
17237 ereport(NOTICE,
17238 (errmsg("relation \"%s\" does not exist, skipping",
17239 stmt->relation->relname)));
17240 return InvalidObjectAddress;
17243 rel = relation_open(relid, NoLock);
17245 oldNspOid = RelationGetNamespace(rel);
17247 /* If it's an owned sequence, disallow moving it by itself. */
17248 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17250 Oid tableId;
17251 int32 colId;
17253 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17254 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17255 ereport(ERROR,
17256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17257 errmsg("cannot move an owned sequence into another schema"),
17258 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17259 RelationGetRelationName(rel),
17260 get_rel_name(tableId))));
17263 /* Get and lock schema OID and check its permissions. */
17264 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17265 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17267 /* common checks on switching namespaces */
17268 CheckSetNamespace(oldNspOid, nspOid);
17270 objsMoved = new_object_addresses();
17271 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17272 free_object_addresses(objsMoved);
17274 ObjectAddressSet(myself, RelationRelationId, relid);
17276 if (oldschema)
17277 *oldschema = oldNspOid;
17279 /* close rel, but keep lock until commit */
17280 relation_close(rel, NoLock);
17282 return myself;
17286 * The guts of relocating a table or materialized view to another namespace:
17287 * besides moving the relation itself, its dependent objects are relocated to
17288 * the new schema.
17290 void
17291 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17292 ObjectAddresses *objsMoved)
17294 Relation classRel;
17296 Assert(objsMoved != NULL);
17298 /* OK, modify the pg_class row and pg_depend entry */
17299 classRel = table_open(RelationRelationId, RowExclusiveLock);
17301 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17302 nspOid, true, objsMoved);
17304 /* Fix the table's row type too, if it has one */
17305 if (OidIsValid(rel->rd_rel->reltype))
17306 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17307 nspOid, false, false, objsMoved);
17309 /* Fix other dependent stuff */
17310 if (rel->rd_rel->relkind == RELKIND_RELATION ||
17311 rel->rd_rel->relkind == RELKIND_MATVIEW ||
17312 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17314 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17315 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17316 objsMoved, AccessExclusiveLock);
17317 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17318 false, objsMoved);
17321 table_close(classRel, RowExclusiveLock);
17325 * The guts of relocating a relation to another namespace: fix the pg_class
17326 * entry, and the pg_depend entry if any. Caller must already have
17327 * opened and write-locked pg_class.
17329 void
17330 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17331 Oid oldNspOid, Oid newNspOid,
17332 bool hasDependEntry,
17333 ObjectAddresses *objsMoved)
17335 HeapTuple classTup;
17336 Form_pg_class classForm;
17337 ObjectAddress thisobj;
17338 bool already_done = false;
17340 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17341 if (!HeapTupleIsValid(classTup))
17342 elog(ERROR, "cache lookup failed for relation %u", relOid);
17343 classForm = (Form_pg_class) GETSTRUCT(classTup);
17345 Assert(classForm->relnamespace == oldNspOid);
17347 thisobj.classId = RelationRelationId;
17348 thisobj.objectId = relOid;
17349 thisobj.objectSubId = 0;
17352 * If the object has already been moved, don't move it again. If it's
17353 * already in the right place, don't move it, but still fire the object
17354 * access hook.
17356 already_done = object_address_present(&thisobj, objsMoved);
17357 if (!already_done && oldNspOid != newNspOid)
17359 /* check for duplicate name (more friendly than unique-index failure) */
17360 if (get_relname_relid(NameStr(classForm->relname),
17361 newNspOid) != InvalidOid)
17362 ereport(ERROR,
17363 (errcode(ERRCODE_DUPLICATE_TABLE),
17364 errmsg("relation \"%s\" already exists in schema \"%s\"",
17365 NameStr(classForm->relname),
17366 get_namespace_name(newNspOid))));
17368 /* classTup is a copy, so OK to scribble on */
17369 classForm->relnamespace = newNspOid;
17371 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
17373 /* Update dependency on schema if caller said so */
17374 if (hasDependEntry &&
17375 changeDependencyFor(RelationRelationId,
17376 relOid,
17377 NamespaceRelationId,
17378 oldNspOid,
17379 newNspOid) != 1)
17380 elog(ERROR, "could not change schema dependency for relation \"%s\"",
17381 NameStr(classForm->relname));
17383 if (!already_done)
17385 add_exact_object_address(&thisobj, objsMoved);
17387 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17390 heap_freetuple(classTup);
17394 * Move all indexes for the specified relation to another namespace.
17396 * Note: we assume adequate permission checking was done by the caller,
17397 * and that the caller has a suitable lock on the owning relation.
17399 static void
17400 AlterIndexNamespaces(Relation classRel, Relation rel,
17401 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17403 List *indexList;
17404 ListCell *l;
17406 indexList = RelationGetIndexList(rel);
17408 foreach(l, indexList)
17410 Oid indexOid = lfirst_oid(l);
17411 ObjectAddress thisobj;
17413 thisobj.classId = RelationRelationId;
17414 thisobj.objectId = indexOid;
17415 thisobj.objectSubId = 0;
17418 * Note: currently, the index will not have its own dependency on the
17419 * namespace, so we don't need to do changeDependencyFor(). There's no
17420 * row type in pg_type, either.
17422 * XXX this objsMoved test may be pointless -- surely we have a single
17423 * dependency link from a relation to each index?
17425 if (!object_address_present(&thisobj, objsMoved))
17427 AlterRelationNamespaceInternal(classRel, indexOid,
17428 oldNspOid, newNspOid,
17429 false, objsMoved);
17430 add_exact_object_address(&thisobj, objsMoved);
17434 list_free(indexList);
17438 * Move all identity and SERIAL-column sequences of the specified relation to another
17439 * namespace.
17441 * Note: we assume adequate permission checking was done by the caller,
17442 * and that the caller has a suitable lock on the owning relation.
17444 static void
17445 AlterSeqNamespaces(Relation classRel, Relation rel,
17446 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17447 LOCKMODE lockmode)
17449 Relation depRel;
17450 SysScanDesc scan;
17451 ScanKeyData key[2];
17452 HeapTuple tup;
17455 * SERIAL sequences are those having an auto dependency on one of the
17456 * table's columns (we don't care *which* column, exactly).
17458 depRel = table_open(DependRelationId, AccessShareLock);
17460 ScanKeyInit(&key[0],
17461 Anum_pg_depend_refclassid,
17462 BTEqualStrategyNumber, F_OIDEQ,
17463 ObjectIdGetDatum(RelationRelationId));
17464 ScanKeyInit(&key[1],
17465 Anum_pg_depend_refobjid,
17466 BTEqualStrategyNumber, F_OIDEQ,
17467 ObjectIdGetDatum(RelationGetRelid(rel)));
17468 /* we leave refobjsubid unspecified */
17470 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17471 NULL, 2, key);
17473 while (HeapTupleIsValid(tup = systable_getnext(scan)))
17475 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17476 Relation seqRel;
17478 /* skip dependencies other than auto dependencies on columns */
17479 if (depForm->refobjsubid == 0 ||
17480 depForm->classid != RelationRelationId ||
17481 depForm->objsubid != 0 ||
17482 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17483 continue;
17485 /* Use relation_open just in case it's an index */
17486 seqRel = relation_open(depForm->objid, lockmode);
17488 /* skip non-sequence relations */
17489 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17491 /* No need to keep the lock */
17492 relation_close(seqRel, lockmode);
17493 continue;
17496 /* Fix the pg_class and pg_depend entries */
17497 AlterRelationNamespaceInternal(classRel, depForm->objid,
17498 oldNspOid, newNspOid,
17499 true, objsMoved);
17502 * Sequences used to have entries in pg_type, but no longer do. If we
17503 * ever re-instate that, we'll need to move the pg_type entry to the
17504 * new namespace, too (using AlterTypeNamespaceInternal).
17506 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
17508 /* Now we can close it. Keep the lock till end of transaction. */
17509 relation_close(seqRel, NoLock);
17512 systable_endscan(scan);
17514 relation_close(depRel, AccessShareLock);
17519 * This code supports
17520 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17522 * Because we only support this for TEMP tables, it's sufficient to remember
17523 * the state in a backend-local data structure.
17527 * Register a newly-created relation's ON COMMIT action.
17529 void
17530 register_on_commit_action(Oid relid, OnCommitAction action)
17532 OnCommitItem *oc;
17533 MemoryContext oldcxt;
17536 * We needn't bother registering the relation unless there is an ON COMMIT
17537 * action we need to take.
17539 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
17540 return;
17542 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
17544 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
17545 oc->relid = relid;
17546 oc->oncommit = action;
17547 oc->creating_subid = GetCurrentSubTransactionId();
17548 oc->deleting_subid = InvalidSubTransactionId;
17551 * We use lcons() here so that ON COMMIT actions are processed in reverse
17552 * order of registration. That might not be essential but it seems
17553 * reasonable.
17555 on_commits = lcons(oc, on_commits);
17557 MemoryContextSwitchTo(oldcxt);
17561 * Unregister any ON COMMIT action when a relation is deleted.
17563 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
17565 void
17566 remove_on_commit_action(Oid relid)
17568 ListCell *l;
17570 foreach(l, on_commits)
17572 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17574 if (oc->relid == relid)
17576 oc->deleting_subid = GetCurrentSubTransactionId();
17577 break;
17583 * Perform ON COMMIT actions.
17585 * This is invoked just before actually committing, since it's possible
17586 * to encounter errors.
17588 void
17589 PreCommit_on_commit_actions(void)
17591 ListCell *l;
17592 List *oids_to_truncate = NIL;
17593 List *oids_to_drop = NIL;
17595 foreach(l, on_commits)
17597 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17599 /* Ignore entry if already dropped in this xact */
17600 if (oc->deleting_subid != InvalidSubTransactionId)
17601 continue;
17603 switch (oc->oncommit)
17605 case ONCOMMIT_NOOP:
17606 case ONCOMMIT_PRESERVE_ROWS:
17607 /* Do nothing (there shouldn't be such entries, actually) */
17608 break;
17609 case ONCOMMIT_DELETE_ROWS:
17612 * If this transaction hasn't accessed any temporary
17613 * relations, we can skip truncating ON COMMIT DELETE ROWS
17614 * tables, as they must still be empty.
17616 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
17617 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
17618 break;
17619 case ONCOMMIT_DROP:
17620 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
17621 break;
17626 * Truncate relations before dropping so that all dependencies between
17627 * relations are removed after they are worked on. Doing it like this
17628 * might be a waste as it is possible that a relation being truncated will
17629 * be dropped anyway due to its parent being dropped, but this makes the
17630 * code more robust because of not having to re-check that the relation
17631 * exists at truncation time.
17633 if (oids_to_truncate != NIL)
17634 heap_truncate(oids_to_truncate);
17636 if (oids_to_drop != NIL)
17638 ObjectAddresses *targetObjects = new_object_addresses();
17640 foreach(l, oids_to_drop)
17642 ObjectAddress object;
17644 object.classId = RelationRelationId;
17645 object.objectId = lfirst_oid(l);
17646 object.objectSubId = 0;
17648 Assert(!object_address_present(&object, targetObjects));
17650 add_exact_object_address(&object, targetObjects);
17654 * Object deletion might involve toast table access (to clean up
17655 * toasted catalog entries), so ensure we have a valid snapshot.
17657 PushActiveSnapshot(GetTransactionSnapshot());
17660 * Since this is an automatic drop, rather than one directly initiated
17661 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17663 performMultipleDeletions(targetObjects, DROP_CASCADE,
17664 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
17666 PopActiveSnapshot();
17668 #ifdef USE_ASSERT_CHECKING
17671 * Note that table deletion will call remove_on_commit_action, so the
17672 * entry should get marked as deleted.
17674 foreach(l, on_commits)
17676 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17678 if (oc->oncommit != ONCOMMIT_DROP)
17679 continue;
17681 Assert(oc->deleting_subid != InvalidSubTransactionId);
17683 #endif
17688 * Post-commit or post-abort cleanup for ON COMMIT management.
17690 * All we do here is remove no-longer-needed OnCommitItem entries.
17692 * During commit, remove entries that were deleted during this transaction;
17693 * during abort, remove those created during this transaction.
17695 void
17696 AtEOXact_on_commit_actions(bool isCommit)
17698 ListCell *cur_item;
17700 foreach(cur_item, on_commits)
17702 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17704 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
17705 oc->creating_subid != InvalidSubTransactionId)
17707 /* cur_item must be removed */
17708 on_commits = foreach_delete_current(on_commits, cur_item);
17709 pfree(oc);
17711 else
17713 /* cur_item must be preserved */
17714 oc->creating_subid = InvalidSubTransactionId;
17715 oc->deleting_subid = InvalidSubTransactionId;
17721 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17723 * During subabort, we can immediately remove entries created during this
17724 * subtransaction. During subcommit, just relabel entries marked during
17725 * this subtransaction as being the parent's responsibility.
17727 void
17728 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
17729 SubTransactionId parentSubid)
17731 ListCell *cur_item;
17733 foreach(cur_item, on_commits)
17735 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17737 if (!isCommit && oc->creating_subid == mySubid)
17739 /* cur_item must be removed */
17740 on_commits = foreach_delete_current(on_commits, cur_item);
17741 pfree(oc);
17743 else
17745 /* cur_item must be preserved */
17746 if (oc->creating_subid == mySubid)
17747 oc->creating_subid = parentSubid;
17748 if (oc->deleting_subid == mySubid)
17749 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
17755 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17756 * the relation to be locked only if (1) it's a plain or partitioned table,
17757 * materialized view, or TOAST table and (2) the current user is the owner (or
17758 * the superuser). This meets the permission-checking needs of CLUSTER,
17759 * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it
17760 * can be used by all.
17762 void
17763 RangeVarCallbackOwnsTable(const RangeVar *relation,
17764 Oid relId, Oid oldRelId, void *arg)
17766 char relkind;
17768 /* Nothing to do if the relation was not found. */
17769 if (!OidIsValid(relId))
17770 return;
17773 * If the relation does exist, check whether it's an index. But note that
17774 * the relation might have been dropped between the time we did the name
17775 * lookup and now. In that case, there's nothing to do.
17777 relkind = get_rel_relkind(relId);
17778 if (!relkind)
17779 return;
17780 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
17781 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
17782 ereport(ERROR,
17783 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17784 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
17786 /* Check permissions */
17787 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17788 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
17792 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17794 static void
17795 RangeVarCallbackForTruncate(const RangeVar *relation,
17796 Oid relId, Oid oldRelId, void *arg)
17798 HeapTuple tuple;
17800 /* Nothing to do if the relation was not found. */
17801 if (!OidIsValid(relId))
17802 return;
17804 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17805 if (!HeapTupleIsValid(tuple)) /* should not happen */
17806 elog(ERROR, "cache lookup failed for relation %u", relId);
17808 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
17809 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17811 ReleaseSysCache(tuple);
17815 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17816 * the owner of the relation, or superuser.
17818 void
17819 RangeVarCallbackOwnsRelation(const RangeVar *relation,
17820 Oid relId, Oid oldRelId, void *arg)
17822 HeapTuple tuple;
17824 /* Nothing to do if the relation was not found. */
17825 if (!OidIsValid(relId))
17826 return;
17828 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17829 if (!HeapTupleIsValid(tuple)) /* should not happen */
17830 elog(ERROR, "cache lookup failed for relation %u", relId);
17832 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17833 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
17834 relation->relname);
17836 if (!allowSystemTableMods &&
17837 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
17838 ereport(ERROR,
17839 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17840 errmsg("permission denied: \"%s\" is a system catalog",
17841 relation->relname)));
17843 ReleaseSysCache(tuple);
17847 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17848 * processing.
17850 static void
17851 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
17852 void *arg)
17854 Node *stmt = (Node *) arg;
17855 ObjectType reltype;
17856 HeapTuple tuple;
17857 Form_pg_class classform;
17858 AclResult aclresult;
17859 char relkind;
17861 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
17862 if (!HeapTupleIsValid(tuple))
17863 return; /* concurrently dropped */
17864 classform = (Form_pg_class) GETSTRUCT(tuple);
17865 relkind = classform->relkind;
17867 /* Must own relation. */
17868 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
17869 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
17871 /* No system table modifications unless explicitly allowed. */
17872 if (!allowSystemTableMods && IsSystemClass(relid, classform))
17873 ereport(ERROR,
17874 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17875 errmsg("permission denied: \"%s\" is a system catalog",
17876 rv->relname)));
17879 * Extract the specified relation type from the statement parse tree.
17881 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17882 * have CREATE rights on the containing namespace.
17884 if (IsA(stmt, RenameStmt))
17886 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17887 GetUserId(), ACL_CREATE);
17888 if (aclresult != ACLCHECK_OK)
17889 aclcheck_error(aclresult, OBJECT_SCHEMA,
17890 get_namespace_name(classform->relnamespace));
17891 reltype = ((RenameStmt *) stmt)->renameType;
17893 else if (IsA(stmt, AlterObjectSchemaStmt))
17894 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17896 else if (IsA(stmt, AlterTableStmt))
17897 reltype = ((AlterTableStmt *) stmt)->objtype;
17898 else
17900 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17901 reltype = OBJECT_TABLE; /* placate compiler */
17905 * For compatibility with prior releases, we allow ALTER TABLE to be used
17906 * with most other types of relations (but not composite types). We allow
17907 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17908 * otherwise. Otherwise, the user must select the correct form of the
17909 * command for the relation at issue.
17911 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
17912 ereport(ERROR,
17913 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17914 errmsg("\"%s\" is not a sequence", rv->relname)));
17916 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
17917 ereport(ERROR,
17918 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17919 errmsg("\"%s\" is not a view", rv->relname)));
17921 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
17922 ereport(ERROR,
17923 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17924 errmsg("\"%s\" is not a materialized view", rv->relname)));
17926 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
17927 ereport(ERROR,
17928 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17929 errmsg("\"%s\" is not a foreign table", rv->relname)));
17931 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
17932 ereport(ERROR,
17933 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17934 errmsg("\"%s\" is not a composite type", rv->relname)));
17936 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17937 relkind != RELKIND_PARTITIONED_INDEX
17938 && !IsA(stmt, RenameStmt))
17939 ereport(ERROR,
17940 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17941 errmsg("\"%s\" is not an index", rv->relname)));
17944 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17945 * TYPE for that.
17947 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
17948 ereport(ERROR,
17949 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17950 errmsg("\"%s\" is a composite type", rv->relname),
17951 /* translator: %s is an SQL ALTER command */
17952 errhint("Use %s instead.",
17953 "ALTER TYPE")));
17956 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17957 * to a different schema, such as indexes and TOAST tables.
17959 if (IsA(stmt, AlterObjectSchemaStmt))
17961 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
17962 ereport(ERROR,
17963 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17964 errmsg("cannot change schema of index \"%s\"",
17965 rv->relname),
17966 errhint("Change the schema of the table instead.")));
17967 else if (relkind == RELKIND_COMPOSITE_TYPE)
17968 ereport(ERROR,
17969 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17970 errmsg("cannot change schema of composite type \"%s\"",
17971 rv->relname),
17972 /* translator: %s is an SQL ALTER command */
17973 errhint("Use %s instead.",
17974 "ALTER TYPE")));
17975 else if (relkind == RELKIND_TOASTVALUE)
17976 ereport(ERROR,
17977 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17978 errmsg("cannot change schema of TOAST table \"%s\"",
17979 rv->relname),
17980 errhint("Change the schema of the table instead.")));
17983 ReleaseSysCache(tuple);
17987 * Transform any expressions present in the partition key
17989 * Returns a transformed PartitionSpec.
17991 static PartitionSpec *
17992 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
17994 PartitionSpec *newspec;
17995 ParseState *pstate;
17996 ParseNamespaceItem *nsitem;
17997 ListCell *l;
17999 newspec = makeNode(PartitionSpec);
18001 newspec->strategy = partspec->strategy;
18002 newspec->partParams = NIL;
18003 newspec->location = partspec->location;
18005 /* Check valid number of columns for strategy */
18006 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18007 list_length(partspec->partParams) != 1)
18008 ereport(ERROR,
18009 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18010 errmsg("cannot use \"list\" partition strategy with more than one column")));
18013 * Create a dummy ParseState and insert the target relation as its sole
18014 * rangetable entry. We need a ParseState for transformExpr.
18016 pstate = make_parsestate(NULL);
18017 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18018 NULL, false, true);
18019 addNSItemToQuery(pstate, nsitem, true, true, true);
18021 /* take care of any partition expressions */
18022 foreach(l, partspec->partParams)
18024 PartitionElem *pelem = lfirst_node(PartitionElem, l);
18026 if (pelem->expr)
18028 /* Copy, to avoid scribbling on the input */
18029 pelem = copyObject(pelem);
18031 /* Now do parse transformation of the expression */
18032 pelem->expr = transformExpr(pstate, pelem->expr,
18033 EXPR_KIND_PARTITION_EXPRESSION);
18035 /* we have to fix its collations too */
18036 assign_expr_collations(pstate, pelem->expr);
18039 newspec->partParams = lappend(newspec->partParams, pelem);
18042 return newspec;
18046 * Compute per-partition-column information from a list of PartitionElems.
18047 * Expressions in the PartitionElems must be parse-analyzed already.
18049 static void
18050 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18051 List **partexprs, Oid *partopclass, Oid *partcollation,
18052 PartitionStrategy strategy)
18054 int attn;
18055 ListCell *lc;
18056 Oid am_oid;
18058 attn = 0;
18059 foreach(lc, partParams)
18061 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18062 Oid atttype;
18063 Oid attcollation;
18065 if (pelem->name != NULL)
18067 /* Simple attribute reference */
18068 HeapTuple atttuple;
18069 Form_pg_attribute attform;
18071 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18072 pelem->name);
18073 if (!HeapTupleIsValid(atttuple))
18074 ereport(ERROR,
18075 (errcode(ERRCODE_UNDEFINED_COLUMN),
18076 errmsg("column \"%s\" named in partition key does not exist",
18077 pelem->name),
18078 parser_errposition(pstate, pelem->location)));
18079 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18081 if (attform->attnum <= 0)
18082 ereport(ERROR,
18083 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18084 errmsg("cannot use system column \"%s\" in partition key",
18085 pelem->name),
18086 parser_errposition(pstate, pelem->location)));
18089 * Generated columns cannot work: They are computed after BEFORE
18090 * triggers, but partition routing is done before all triggers.
18092 if (attform->attgenerated)
18093 ereport(ERROR,
18094 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18095 errmsg("cannot use generated column in partition key"),
18096 errdetail("Column \"%s\" is a generated column.",
18097 pelem->name),
18098 parser_errposition(pstate, pelem->location)));
18100 partattrs[attn] = attform->attnum;
18101 atttype = attform->atttypid;
18102 attcollation = attform->attcollation;
18103 ReleaseSysCache(atttuple);
18105 else
18107 /* Expression */
18108 Node *expr = pelem->expr;
18109 char partattname[16];
18111 Assert(expr != NULL);
18112 atttype = exprType(expr);
18113 attcollation = exprCollation(expr);
18116 * The expression must be of a storable type (e.g., not RECORD).
18117 * The test is the same as for whether a table column is of a safe
18118 * type (which is why we needn't check for the non-expression
18119 * case).
18121 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18122 CheckAttributeType(partattname,
18123 atttype, attcollation,
18124 NIL, CHKATYPE_IS_PARTKEY);
18127 * Strip any top-level COLLATE clause. This ensures that we treat
18128 * "x COLLATE y" and "(x COLLATE y)" alike.
18130 while (IsA(expr, CollateExpr))
18131 expr = (Node *) ((CollateExpr *) expr)->arg;
18133 if (IsA(expr, Var) &&
18134 ((Var *) expr)->varattno > 0)
18137 * User wrote "(column)" or "(column COLLATE something)".
18138 * Treat it like simple attribute anyway.
18140 partattrs[attn] = ((Var *) expr)->varattno;
18142 else
18144 Bitmapset *expr_attrs = NULL;
18145 int i;
18147 partattrs[attn] = 0; /* marks the column as expression */
18148 *partexprs = lappend(*partexprs, expr);
18151 * Try to simplify the expression before checking for
18152 * mutability. The main practical value of doing it in this
18153 * order is that an inline-able SQL-language function will be
18154 * accepted if its expansion is immutable, whether or not the
18155 * function itself is marked immutable.
18157 * Note that expression_planner does not change the passed in
18158 * expression destructively and we have already saved the
18159 * expression to be stored into the catalog above.
18161 expr = (Node *) expression_planner((Expr *) expr);
18164 * Partition expression cannot contain mutable functions,
18165 * because a given row must always map to the same partition
18166 * as long as there is no change in the partition boundary
18167 * structure.
18169 if (contain_mutable_functions(expr))
18170 ereport(ERROR,
18171 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18172 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18175 * transformPartitionSpec() should have already rejected
18176 * subqueries, aggregates, window functions, and SRFs, based
18177 * on the EXPR_KIND_ for partition expressions.
18181 * Cannot allow system column references, since that would
18182 * make partition routing impossible: their values won't be
18183 * known yet when we need to do that.
18185 pull_varattnos(expr, 1, &expr_attrs);
18186 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18188 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18189 expr_attrs))
18190 ereport(ERROR,
18191 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18192 errmsg("partition key expressions cannot contain system column references")));
18196 * Generated columns cannot work: They are computed after
18197 * BEFORE triggers, but partition routing is done before all
18198 * triggers.
18200 i = -1;
18201 while ((i = bms_next_member(expr_attrs, i)) >= 0)
18203 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18205 if (attno > 0 &&
18206 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18207 ereport(ERROR,
18208 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18209 errmsg("cannot use generated column in partition key"),
18210 errdetail("Column \"%s\" is a generated column.",
18211 get_attname(RelationGetRelid(rel), attno, false)),
18212 parser_errposition(pstate, pelem->location)));
18216 * While it is not exactly *wrong* for a partition expression
18217 * to be a constant, it seems better to reject such keys.
18219 if (IsA(expr, Const))
18220 ereport(ERROR,
18221 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18222 errmsg("cannot use constant expression as partition key")));
18227 * Apply collation override if any
18229 if (pelem->collation)
18230 attcollation = get_collation_oid(pelem->collation, false);
18233 * Check we have a collation iff it's a collatable type. The only
18234 * expected failures here are (1) COLLATE applied to a noncollatable
18235 * type, or (2) partition expression had an unresolved collation. But
18236 * we might as well code this to be a complete consistency check.
18238 if (type_is_collatable(atttype))
18240 if (!OidIsValid(attcollation))
18241 ereport(ERROR,
18242 (errcode(ERRCODE_INDETERMINATE_COLLATION),
18243 errmsg("could not determine which collation to use for partition expression"),
18244 errhint("Use the COLLATE clause to set the collation explicitly.")));
18246 else
18248 if (OidIsValid(attcollation))
18249 ereport(ERROR,
18250 (errcode(ERRCODE_DATATYPE_MISMATCH),
18251 errmsg("collations are not supported by type %s",
18252 format_type_be(atttype))));
18255 partcollation[attn] = attcollation;
18258 * Identify the appropriate operator class. For list and range
18259 * partitioning, we use a btree operator class; hash partitioning uses
18260 * a hash operator class.
18262 if (strategy == PARTITION_STRATEGY_HASH)
18263 am_oid = HASH_AM_OID;
18264 else
18265 am_oid = BTREE_AM_OID;
18267 if (!pelem->opclass)
18269 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18271 if (!OidIsValid(partopclass[attn]))
18273 if (strategy == PARTITION_STRATEGY_HASH)
18274 ereport(ERROR,
18275 (errcode(ERRCODE_UNDEFINED_OBJECT),
18276 errmsg("data type %s has no default operator class for access method \"%s\"",
18277 format_type_be(atttype), "hash"),
18278 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18279 else
18280 ereport(ERROR,
18281 (errcode(ERRCODE_UNDEFINED_OBJECT),
18282 errmsg("data type %s has no default operator class for access method \"%s\"",
18283 format_type_be(atttype), "btree"),
18284 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18287 else
18288 partopclass[attn] = ResolveOpClass(pelem->opclass,
18289 atttype,
18290 am_oid == HASH_AM_OID ? "hash" : "btree",
18291 am_oid);
18293 attn++;
18298 * PartConstraintImpliedByRelConstraint
18299 * Do scanrel's existing constraints imply the partition constraint?
18301 * "Existing constraints" include its check constraints and column-level
18302 * not-null constraints. partConstraint describes the partition constraint,
18303 * in implicit-AND form.
18305 bool
18306 PartConstraintImpliedByRelConstraint(Relation scanrel,
18307 List *partConstraint)
18309 List *existConstraint = NIL;
18310 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18311 int i;
18313 if (constr && constr->has_not_null)
18315 int natts = scanrel->rd_att->natts;
18317 for (i = 1; i <= natts; i++)
18319 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18321 if (att->attnotnull && !att->attisdropped)
18323 NullTest *ntest = makeNode(NullTest);
18325 ntest->arg = (Expr *) makeVar(1,
18327 att->atttypid,
18328 att->atttypmod,
18329 att->attcollation,
18331 ntest->nulltesttype = IS_NOT_NULL;
18334 * argisrow=false is correct even for a composite column,
18335 * because attnotnull does not represent a SQL-spec IS NOT
18336 * NULL test in such a case, just IS DISTINCT FROM NULL.
18338 ntest->argisrow = false;
18339 ntest->location = -1;
18340 existConstraint = lappend(existConstraint, ntest);
18345 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18349 * ConstraintImpliedByRelConstraint
18350 * Do scanrel's existing constraints imply the given constraint?
18352 * testConstraint is the constraint to validate. provenConstraint is a
18353 * caller-provided list of conditions which this function may assume
18354 * to be true. Both provenConstraint and testConstraint must be in
18355 * implicit-AND form, must only contain immutable clauses, and must
18356 * contain only Vars with varno = 1.
18358 bool
18359 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18361 List *existConstraint = list_copy(provenConstraint);
18362 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18363 int num_check,
18366 num_check = (constr != NULL) ? constr->num_check : 0;
18367 for (i = 0; i < num_check; i++)
18369 Node *cexpr;
18372 * If this constraint hasn't been fully validated yet, we must ignore
18373 * it here.
18375 if (!constr->check[i].ccvalid)
18376 continue;
18378 cexpr = stringToNode(constr->check[i].ccbin);
18381 * Run each expression through const-simplification and
18382 * canonicalization. It is necessary, because we will be comparing it
18383 * to similarly-processed partition constraint expressions, and may
18384 * fail to detect valid matches without this.
18386 cexpr = eval_const_expressions(NULL, cexpr);
18387 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18389 existConstraint = list_concat(existConstraint,
18390 make_ands_implicit((Expr *) cexpr));
18394 * Try to make the proof. Since we are comparing CHECK constraints, we
18395 * need to use weak implication, i.e., we assume existConstraint is
18396 * not-false and try to prove the same for testConstraint.
18398 * Note that predicate_implied_by assumes its first argument is known
18399 * immutable. That should always be true for both NOT NULL and partition
18400 * constraints, so we don't test it here.
18402 return predicate_implied_by(testConstraint, existConstraint, true);
18406 * QueuePartitionConstraintValidation
18408 * Add an entry to wqueue to have the given partition constraint validated by
18409 * Phase 3, for the given relation, and all its children.
18411 * We first verify whether the given constraint is implied by pre-existing
18412 * relation constraints; if it is, there's no need to scan the table to
18413 * validate, so don't queue in that case.
18415 static void
18416 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18417 List *partConstraint,
18418 bool validate_default)
18421 * Based on the table's existing constraints, determine whether or not we
18422 * may skip scanning the table.
18424 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18426 if (!validate_default)
18427 ereport(DEBUG1,
18428 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18429 RelationGetRelationName(scanrel))));
18430 else
18431 ereport(DEBUG1,
18432 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18433 RelationGetRelationName(scanrel))));
18434 return;
18438 * Constraints proved insufficient. For plain relations, queue a
18439 * validation item now; for partitioned tables, recurse to process each
18440 * partition.
18442 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18444 AlteredTableInfo *tab;
18446 /* Grab a work queue entry. */
18447 tab = ATGetQueueEntry(wqueue, scanrel);
18448 Assert(tab->partition_constraint == NULL);
18449 tab->partition_constraint = (Expr *) linitial(partConstraint);
18450 tab->validate_default = validate_default;
18452 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18454 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
18455 int i;
18457 for (i = 0; i < partdesc->nparts; i++)
18459 Relation part_rel;
18460 List *thisPartConstraint;
18463 * This is the minimum lock we need to prevent deadlocks.
18465 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18468 * Adjust the constraint for scanrel so that it matches this
18469 * partition's attribute numbers.
18471 thisPartConstraint =
18472 map_partition_varattnos(partConstraint, 1,
18473 part_rel, scanrel);
18475 QueuePartitionConstraintValidation(wqueue, part_rel,
18476 thisPartConstraint,
18477 validate_default);
18478 table_close(part_rel, NoLock); /* keep lock till commit */
18484 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18486 * Return the address of the newly attached partition.
18488 static ObjectAddress
18489 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
18490 AlterTableUtilityContext *context)
18492 Relation attachrel,
18493 catalog;
18494 List *attachrel_children;
18495 List *partConstraint;
18496 SysScanDesc scan;
18497 ScanKeyData skey;
18498 AttrNumber attno;
18499 int natts;
18500 TupleDesc tupleDesc;
18501 ObjectAddress address;
18502 const char *trigger_name;
18503 Oid defaultPartOid;
18504 List *partBoundConstraint;
18505 ParseState *pstate = make_parsestate(NULL);
18507 pstate->p_sourcetext = context->queryString;
18510 * We must lock the default partition if one exists, because attaching a
18511 * new partition will change its partition constraint.
18513 defaultPartOid =
18514 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
18515 if (OidIsValid(defaultPartOid))
18516 LockRelationOid(defaultPartOid, AccessExclusiveLock);
18518 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
18521 * XXX I think it'd be a good idea to grab locks on all tables referenced
18522 * by FKs at this point also.
18526 * Must be owner of both parent and source table -- parent was checked by
18527 * ATSimplePermissions call in ATPrepCmd
18529 ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
18531 /* A partition can only have one parent */
18532 if (attachrel->rd_rel->relispartition)
18533 ereport(ERROR,
18534 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18535 errmsg("\"%s\" is already a partition",
18536 RelationGetRelationName(attachrel))));
18538 if (OidIsValid(attachrel->rd_rel->reloftype))
18539 ereport(ERROR,
18540 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18541 errmsg("cannot attach a typed table as partition")));
18544 * Table being attached should not already be part of inheritance; either
18545 * as a child table...
18547 catalog = table_open(InheritsRelationId, AccessShareLock);
18548 ScanKeyInit(&skey,
18549 Anum_pg_inherits_inhrelid,
18550 BTEqualStrategyNumber, F_OIDEQ,
18551 ObjectIdGetDatum(RelationGetRelid(attachrel)));
18552 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
18553 NULL, 1, &skey);
18554 if (HeapTupleIsValid(systable_getnext(scan)))
18555 ereport(ERROR,
18556 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18557 errmsg("cannot attach inheritance child as partition")));
18558 systable_endscan(scan);
18560 /* ...or as a parent table (except the case when it is partitioned) */
18561 ScanKeyInit(&skey,
18562 Anum_pg_inherits_inhparent,
18563 BTEqualStrategyNumber, F_OIDEQ,
18564 ObjectIdGetDatum(RelationGetRelid(attachrel)));
18565 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
18566 1, &skey);
18567 if (HeapTupleIsValid(systable_getnext(scan)) &&
18568 attachrel->rd_rel->relkind == RELKIND_RELATION)
18569 ereport(ERROR,
18570 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18571 errmsg("cannot attach inheritance parent as partition")));
18572 systable_endscan(scan);
18573 table_close(catalog, AccessShareLock);
18576 * Prevent circularity by seeing if rel is a partition of attachrel. (In
18577 * particular, this disallows making a rel a partition of itself.)
18579 * We do that by checking if rel is a member of the list of attachrel's
18580 * partitions provided the latter is partitioned at all. We want to avoid
18581 * having to construct this list again, so we request the strongest lock
18582 * on all partitions. We need the strongest lock, because we may decide
18583 * to scan them if we find out that the table being attached (or its leaf
18584 * partitions) may contain rows that violate the partition constraint. If
18585 * the table has a constraint that would prevent such rows, which by
18586 * definition is present in all the partitions, we need not scan the
18587 * table, nor its partitions. But we cannot risk a deadlock by taking a
18588 * weaker lock now and the stronger one only when needed.
18590 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
18591 AccessExclusiveLock, NULL);
18592 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
18593 ereport(ERROR,
18594 (errcode(ERRCODE_DUPLICATE_TABLE),
18595 errmsg("circular inheritance not allowed"),
18596 errdetail("\"%s\" is already a child of \"%s\".",
18597 RelationGetRelationName(rel),
18598 RelationGetRelationName(attachrel))));
18600 /* If the parent is permanent, so must be all of its partitions. */
18601 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
18602 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
18603 ereport(ERROR,
18604 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18605 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18606 RelationGetRelationName(rel))));
18608 /* Temp parent cannot have a partition that is itself not a temp */
18609 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18610 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
18611 ereport(ERROR,
18612 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18613 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18614 RelationGetRelationName(rel))));
18616 /* If the parent is temp, it must belong to this session */
18617 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18618 !rel->rd_islocaltemp)
18619 ereport(ERROR,
18620 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18621 errmsg("cannot attach as partition of temporary relation of another session")));
18623 /* Ditto for the partition */
18624 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18625 !attachrel->rd_islocaltemp)
18626 ereport(ERROR,
18627 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18628 errmsg("cannot attach temporary relation of another session as partition")));
18630 /* Check if there are any columns in attachrel that aren't in the parent */
18631 tupleDesc = RelationGetDescr(attachrel);
18632 natts = tupleDesc->natts;
18633 for (attno = 1; attno <= natts; attno++)
18635 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
18636 char *attributeName = NameStr(attribute->attname);
18638 /* Ignore dropped */
18639 if (attribute->attisdropped)
18640 continue;
18642 /* Try to find the column in parent (matching on column name) */
18643 if (!SearchSysCacheExists2(ATTNAME,
18644 ObjectIdGetDatum(RelationGetRelid(rel)),
18645 CStringGetDatum(attributeName)))
18646 ereport(ERROR,
18647 (errcode(ERRCODE_DATATYPE_MISMATCH),
18648 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18649 RelationGetRelationName(attachrel), attributeName,
18650 RelationGetRelationName(rel)),
18651 errdetail("The new partition may contain only the columns present in parent.")));
18655 * If child_rel has row-level triggers with transition tables, we
18656 * currently don't allow it to become a partition. See also prohibitions
18657 * in ATExecAddInherit() and CreateTrigger().
18659 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
18660 if (trigger_name != NULL)
18661 ereport(ERROR,
18662 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18663 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18664 trigger_name, RelationGetRelationName(attachrel)),
18665 errdetail("ROW triggers with transition tables are not supported on partitions.")));
18668 * Check that the new partition's bound is valid and does not overlap any
18669 * of existing partitions of the parent - note that it does not return on
18670 * error.
18672 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
18673 cmd->bound, pstate);
18675 /* OK to create inheritance. Rest of the checks performed there */
18676 CreateInheritance(attachrel, rel);
18678 /* Update the pg_class entry. */
18679 StorePartitionBound(attachrel, rel, cmd->bound);
18681 /* Ensure there exists a correct set of indexes in the partition. */
18682 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
18684 /* and triggers */
18685 CloneRowTriggersToPartition(rel, attachrel);
18688 * Clone foreign key constraints. Callee is responsible for setting up
18689 * for phase 3 constraint verification.
18691 CloneForeignKeyConstraints(wqueue, rel, attachrel);
18694 * Generate partition constraint from the partition bound specification.
18695 * If the parent itself is a partition, make sure to include its
18696 * constraint as well.
18698 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
18699 partConstraint = list_concat(partBoundConstraint,
18700 RelationGetPartitionQual(rel));
18702 /* Skip validation if there are no constraints to validate. */
18703 if (partConstraint)
18706 * Run the partition quals through const-simplification similar to
18707 * check constraints. We skip canonicalize_qual, though, because
18708 * partition quals should be in canonical form already.
18710 partConstraint =
18711 (List *) eval_const_expressions(NULL,
18712 (Node *) partConstraint);
18714 /* XXX this sure looks wrong */
18715 partConstraint = list_make1(make_ands_explicit(partConstraint));
18718 * Adjust the generated constraint to match this partition's attribute
18719 * numbers.
18721 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
18722 rel);
18724 /* Validate partition constraints against the table being attached. */
18725 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
18726 false);
18730 * If we're attaching a partition other than the default partition and a
18731 * default one exists, then that partition's partition constraint changes,
18732 * so add an entry to the work queue to validate it, too. (We must not do
18733 * this when the partition being attached is the default one; we already
18734 * did it above!)
18736 if (OidIsValid(defaultPartOid))
18738 Relation defaultrel;
18739 List *defPartConstraint;
18741 Assert(!cmd->bound->is_default);
18743 /* we already hold a lock on the default partition */
18744 defaultrel = table_open(defaultPartOid, NoLock);
18745 defPartConstraint =
18746 get_proposed_default_constraint(partBoundConstraint);
18749 * Map the Vars in the constraint expression from rel's attnos to
18750 * defaultrel's.
18752 defPartConstraint =
18753 map_partition_varattnos(defPartConstraint,
18754 1, defaultrel, rel);
18755 QueuePartitionConstraintValidation(wqueue, defaultrel,
18756 defPartConstraint, true);
18758 /* keep our lock until commit. */
18759 table_close(defaultrel, NoLock);
18762 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
18765 * If the partition we just attached is partitioned itself, invalidate
18766 * relcache for all descendent partitions too to ensure that their
18767 * rd_partcheck expression trees are rebuilt; partitions already locked at
18768 * the beginning of this function.
18770 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18772 ListCell *l;
18774 foreach(l, attachrel_children)
18776 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
18780 /* keep our lock until commit */
18781 table_close(attachrel, NoLock);
18783 return address;
18787 * AttachPartitionEnsureIndexes
18788 * subroutine for ATExecAttachPartition to create/match indexes
18790 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18791 * PARTITION: every partition must have an index attached to each index on the
18792 * partitioned table.
18794 static void
18795 AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
18797 List *idxes;
18798 List *attachRelIdxs;
18799 Relation *attachrelIdxRels;
18800 IndexInfo **attachInfos;
18801 ListCell *cell;
18802 MemoryContext cxt;
18803 MemoryContext oldcxt;
18805 cxt = AllocSetContextCreate(CurrentMemoryContext,
18806 "AttachPartitionEnsureIndexes",
18807 ALLOCSET_DEFAULT_SIZES);
18808 oldcxt = MemoryContextSwitchTo(cxt);
18810 idxes = RelationGetIndexList(rel);
18811 attachRelIdxs = RelationGetIndexList(attachrel);
18812 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18813 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18815 /* Build arrays of all existing indexes and their IndexInfos */
18816 foreach(cell, attachRelIdxs)
18818 Oid cldIdxId = lfirst_oid(cell);
18819 int i = foreach_current_index(cell);
18821 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18822 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18826 * If we're attaching a foreign table, we must fail if any of the indexes
18827 * is a constraint index; otherwise, there's nothing to do here. Do this
18828 * before starting work, to avoid wasting the effort of building a few
18829 * non-unique indexes before coming across a unique one.
18831 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18833 foreach(cell, idxes)
18835 Oid idx = lfirst_oid(cell);
18836 Relation idxRel = index_open(idx, AccessShareLock);
18838 if (idxRel->rd_index->indisunique ||
18839 idxRel->rd_index->indisprimary)
18840 ereport(ERROR,
18841 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18842 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18843 RelationGetRelationName(attachrel),
18844 RelationGetRelationName(rel)),
18845 errdetail("Partitioned table \"%s\" contains unique indexes.",
18846 RelationGetRelationName(rel))));
18847 index_close(idxRel, AccessShareLock);
18850 goto out;
18854 * For each index on the partitioned table, find a matching one in the
18855 * partition-to-be; if one is not found, create one.
18857 foreach(cell, idxes)
18859 Oid idx = lfirst_oid(cell);
18860 Relation idxRel = index_open(idx, AccessShareLock);
18861 IndexInfo *info;
18862 AttrMap *attmap;
18863 bool found = false;
18864 Oid constraintOid;
18867 * Ignore indexes in the partitioned table other than partitioned
18868 * indexes.
18870 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18872 index_close(idxRel, AccessShareLock);
18873 continue;
18876 /* construct an indexinfo to compare existing indexes against */
18877 info = BuildIndexInfo(idxRel);
18878 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18879 RelationGetDescr(rel),
18880 false);
18881 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18884 * Scan the list of existing indexes in the partition-to-be, and mark
18885 * the first matching, valid, unattached one we find, if any, as
18886 * partition of the parent index. If we find one, we're done.
18888 for (int i = 0; i < list_length(attachRelIdxs); i++)
18890 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18891 Oid cldConstrOid = InvalidOid;
18893 /* does this index have a parent? if so, can't use it */
18894 if (attachrelIdxRels[i]->rd_rel->relispartition)
18895 continue;
18897 /* If this index is invalid, can't use it */
18898 if (!attachrelIdxRels[i]->rd_index->indisvalid)
18899 continue;
18901 if (CompareIndexInfo(attachInfos[i], info,
18902 attachrelIdxRels[i]->rd_indcollation,
18903 idxRel->rd_indcollation,
18904 attachrelIdxRels[i]->rd_opfamily,
18905 idxRel->rd_opfamily,
18906 attmap))
18909 * If this index is being created in the parent because of a
18910 * constraint, then the child needs to have a constraint also,
18911 * so look for one. If there is no such constraint, this
18912 * index is no good, so keep looking.
18914 if (OidIsValid(constraintOid))
18916 cldConstrOid =
18917 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
18918 cldIdxId);
18919 /* no dice */
18920 if (!OidIsValid(cldConstrOid))
18921 continue;
18924 /* bingo. */
18925 IndexSetParentIndex(attachrelIdxRels[i], idx);
18926 if (OidIsValid(constraintOid))
18927 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18928 RelationGetRelid(attachrel));
18929 found = true;
18931 CommandCounterIncrement();
18932 break;
18937 * If no suitable index was found in the partition-to-be, create one
18938 * now.
18940 if (!found)
18942 IndexStmt *stmt;
18943 Oid conOid;
18945 stmt = generateClonedIndexStmt(NULL,
18946 idxRel, attmap,
18947 &conOid);
18950 * If the index is a primary key, mark all columns as NOT NULL if
18951 * they aren't already.
18953 if (stmt->primary)
18955 MemoryContextSwitchTo(oldcxt);
18956 for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
18958 AttrNumber childattno;
18960 childattno = get_attnum(RelationGetRelid(attachrel),
18961 get_attname(RelationGetRelid(rel),
18962 info->ii_IndexAttrNumbers[j],
18963 false));
18964 set_attnotnull(wqueue, attachrel, childattno,
18965 true, AccessExclusiveLock);
18967 MemoryContextSwitchTo(cxt);
18970 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
18971 RelationGetRelid(idxRel),
18972 conOid,
18974 true, false, false, false, false);
18977 index_close(idxRel, AccessShareLock);
18980 out:
18981 /* Clean up. */
18982 for (int i = 0; i < list_length(attachRelIdxs); i++)
18983 index_close(attachrelIdxRels[i], AccessShareLock);
18984 MemoryContextSwitchTo(oldcxt);
18985 MemoryContextDelete(cxt);
18989 * CloneRowTriggersToPartition
18990 * subroutine for ATExecAttachPartition/DefineRelation to create row
18991 * triggers on partitions
18993 static void
18994 CloneRowTriggersToPartition(Relation parent, Relation partition)
18996 Relation pg_trigger;
18997 ScanKeyData key;
18998 SysScanDesc scan;
18999 HeapTuple tuple;
19000 MemoryContext perTupCxt;
19002 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19003 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19004 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19005 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19006 true, NULL, 1, &key);
19008 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19009 "clone trig", ALLOCSET_SMALL_SIZES);
19011 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19013 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19014 CreateTrigStmt *trigStmt;
19015 Node *qual = NULL;
19016 Datum value;
19017 bool isnull;
19018 List *cols = NIL;
19019 List *trigargs = NIL;
19020 MemoryContext oldcxt;
19023 * Ignore statement-level triggers; those are not cloned.
19025 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19026 continue;
19029 * Don't clone internal triggers, because the constraint cloning code
19030 * will.
19032 if (trigForm->tgisinternal)
19033 continue;
19036 * Complain if we find an unexpected trigger type.
19038 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19039 !TRIGGER_FOR_AFTER(trigForm->tgtype))
19040 elog(ERROR, "unexpected trigger \"%s\" found",
19041 NameStr(trigForm->tgname));
19043 /* Use short-lived context for CREATE TRIGGER */
19044 oldcxt = MemoryContextSwitchTo(perTupCxt);
19047 * If there is a WHEN clause, generate a 'cooked' version of it that's
19048 * appropriate for the partition.
19050 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19051 RelationGetDescr(pg_trigger), &isnull);
19052 if (!isnull)
19054 qual = stringToNode(TextDatumGetCString(value));
19055 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19056 partition, parent);
19057 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19058 partition, parent);
19062 * If there is a column list, transform it to a list of column names.
19063 * Note we don't need to map this list in any way ...
19065 if (trigForm->tgattr.dim1 > 0)
19067 int i;
19069 for (i = 0; i < trigForm->tgattr.dim1; i++)
19071 Form_pg_attribute col;
19073 col = TupleDescAttr(parent->rd_att,
19074 trigForm->tgattr.values[i] - 1);
19075 cols = lappend(cols,
19076 makeString(pstrdup(NameStr(col->attname))));
19080 /* Reconstruct trigger arguments list. */
19081 if (trigForm->tgnargs > 0)
19083 char *p;
19085 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19086 RelationGetDescr(pg_trigger), &isnull);
19087 if (isnull)
19088 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19089 NameStr(trigForm->tgname), RelationGetRelationName(partition));
19091 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19093 for (int i = 0; i < trigForm->tgnargs; i++)
19095 trigargs = lappend(trigargs, makeString(pstrdup(p)));
19096 p += strlen(p) + 1;
19100 trigStmt = makeNode(CreateTrigStmt);
19101 trigStmt->replace = false;
19102 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19103 trigStmt->trigname = NameStr(trigForm->tgname);
19104 trigStmt->relation = NULL;
19105 trigStmt->funcname = NULL; /* passed separately */
19106 trigStmt->args = trigargs;
19107 trigStmt->row = true;
19108 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19109 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19110 trigStmt->columns = cols;
19111 trigStmt->whenClause = NULL; /* passed separately */
19112 trigStmt->transitionRels = NIL; /* not supported at present */
19113 trigStmt->deferrable = trigForm->tgdeferrable;
19114 trigStmt->initdeferred = trigForm->tginitdeferred;
19115 trigStmt->constrrel = NULL; /* passed separately */
19117 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19118 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19119 trigForm->tgfoid, trigForm->oid, qual,
19120 false, true, trigForm->tgenabled);
19122 MemoryContextSwitchTo(oldcxt);
19123 MemoryContextReset(perTupCxt);
19126 MemoryContextDelete(perTupCxt);
19128 systable_endscan(scan);
19129 table_close(pg_trigger, RowExclusiveLock);
19133 * ALTER TABLE DETACH PARTITION
19135 * Return the address of the relation that is no longer a partition of rel.
19137 * If concurrent mode is requested, we run in two transactions. A side-
19138 * effect is that this command cannot run in a multi-part ALTER TABLE.
19139 * Currently, that's enforced by the grammar.
19141 * The strategy for concurrency is to first modify the partition's
19142 * pg_inherit catalog row to make it visible to everyone that the
19143 * partition is detached, lock the partition against writes, and commit
19144 * the transaction; anyone who requests the partition descriptor from
19145 * that point onwards has to ignore such a partition. In a second
19146 * transaction, we wait until all transactions that could have seen the
19147 * partition as attached are gone, then we remove the rest of partition
19148 * metadata (pg_inherits and pg_class.relpartbounds).
19150 static ObjectAddress
19151 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19152 RangeVar *name, bool concurrent)
19154 Relation partRel;
19155 ObjectAddress address;
19156 Oid defaultPartOid;
19159 * We must lock the default partition, because detaching this partition
19160 * will change its partition constraint.
19162 defaultPartOid =
19163 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19164 if (OidIsValid(defaultPartOid))
19167 * Concurrent detaching when a default partition exists is not
19168 * supported. The main problem is that the default partition
19169 * constraint would change. And there's a definitional problem: what
19170 * should happen to the tuples that are being inserted that belong to
19171 * the partition being detached? Putting them on the partition being
19172 * detached would be wrong, since they'd become "lost" after the
19173 * detaching completes but we cannot put them in the default partition
19174 * either until we alter its partition constraint.
19176 * I think we could solve this problem if we effected the constraint
19177 * change before committing the first transaction. But the lock would
19178 * have to remain AEL and it would cause concurrent query planning to
19179 * be blocked, so changing it that way would be even worse.
19181 if (concurrent)
19182 ereport(ERROR,
19183 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19184 errmsg("cannot detach partitions concurrently when a default partition exists")));
19185 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19189 * In concurrent mode, the partition is locked with share-update-exclusive
19190 * in the first transaction. This allows concurrent transactions to be
19191 * doing DML to the partition.
19193 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19194 AccessExclusiveLock);
19197 * Check inheritance conditions and either delete the pg_inherits row (in
19198 * non-concurrent mode) or just set the inhdetachpending flag.
19200 if (!concurrent)
19201 RemoveInheritance(partRel, rel, false);
19202 else
19203 MarkInheritDetached(partRel, rel);
19206 * Ensure that foreign keys still hold after this detach. This keeps
19207 * locks on the referencing tables, which prevents concurrent transactions
19208 * from adding rows that we wouldn't see. For this to work in concurrent
19209 * mode, it is critical that the partition appears as no longer attached
19210 * for the RI queries as soon as the first transaction commits.
19212 ATDetachCheckNoForeignKeyRefs(partRel);
19215 * Concurrent mode has to work harder; first we add a new constraint to
19216 * the partition that matches the partition constraint. Then we close our
19217 * existing transaction, and in a new one wait for all processes to catch
19218 * up on the catalog updates we've done so far; at that point we can
19219 * complete the operation.
19221 if (concurrent)
19223 Oid partrelid,
19224 parentrelid;
19225 LOCKTAG tag;
19226 char *parentrelname;
19227 char *partrelname;
19230 * Add a new constraint to the partition being detached, which
19231 * supplants the partition constraint (unless there is one already).
19233 DetachAddConstraintIfNeeded(wqueue, partRel);
19236 * We're almost done now; the only traces that remain are the
19237 * pg_inherits tuple and the partition's relpartbounds. Before we can
19238 * remove those, we need to wait until all transactions that know that
19239 * this is a partition are gone.
19243 * Remember relation OIDs to re-acquire them later; and relation names
19244 * too, for error messages if something is dropped in between.
19246 partrelid = RelationGetRelid(partRel);
19247 parentrelid = RelationGetRelid(rel);
19248 parentrelname = MemoryContextStrdup(PortalContext,
19249 RelationGetRelationName(rel));
19250 partrelname = MemoryContextStrdup(PortalContext,
19251 RelationGetRelationName(partRel));
19253 /* Invalidate relcache entries for the parent -- must be before close */
19254 CacheInvalidateRelcache(rel);
19256 table_close(partRel, NoLock);
19257 table_close(rel, NoLock);
19258 tab->rel = NULL;
19260 /* Make updated catalog entry visible */
19261 PopActiveSnapshot();
19262 CommitTransactionCommand();
19264 StartTransactionCommand();
19267 * Now wait. This ensures that all queries that were planned
19268 * including the partition are finished before we remove the rest of
19269 * catalog entries. We don't need or indeed want to acquire this
19270 * lock, though -- that would block later queries.
19272 * We don't need to concern ourselves with waiting for a lock on the
19273 * partition itself, since we will acquire AccessExclusiveLock below.
19275 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19276 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19279 * Now acquire locks in both relations again. Note they may have been
19280 * removed in the meantime, so care is required.
19282 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19283 partRel = try_relation_open(partrelid, AccessExclusiveLock);
19285 /* If the relations aren't there, something bad happened; bail out */
19286 if (rel == NULL)
19288 if (partRel != NULL) /* shouldn't happen */
19289 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19290 partrelname);
19291 ereport(ERROR,
19292 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19293 errmsg("partitioned table \"%s\" was removed concurrently",
19294 parentrelname)));
19296 if (partRel == NULL)
19297 ereport(ERROR,
19298 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19299 errmsg("partition \"%s\" was removed concurrently", partrelname)));
19301 tab->rel = rel;
19304 /* Do the final part of detaching */
19305 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19307 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19309 /* keep our lock until commit */
19310 table_close(partRel, NoLock);
19312 return address;
19316 * Second part of ALTER TABLE .. DETACH.
19318 * This is separate so that it can be run independently when the second
19319 * transaction of the concurrent algorithm fails (crash or abort).
19321 static void
19322 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19323 Oid defaultPartOid)
19325 Relation classRel;
19326 List *fks;
19327 ListCell *cell;
19328 List *indexes;
19329 Datum new_val[Natts_pg_class];
19330 bool new_null[Natts_pg_class],
19331 new_repl[Natts_pg_class];
19332 HeapTuple tuple,
19333 newtuple;
19334 Relation trigrel = NULL;
19336 if (concurrent)
19339 * We can remove the pg_inherits row now. (In the non-concurrent case,
19340 * this was already done).
19342 RemoveInheritance(partRel, rel, true);
19345 /* Drop any triggers that were cloned on creation/attach. */
19346 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19349 * Detach any foreign keys that are inherited. This includes creating
19350 * additional action triggers.
19352 fks = copyObject(RelationGetFKeyList(partRel));
19353 if (fks != NIL)
19354 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19355 foreach(cell, fks)
19357 ForeignKeyCacheInfo *fk = lfirst(cell);
19358 HeapTuple contup;
19359 Form_pg_constraint conform;
19360 Constraint *fkconstraint;
19361 Oid insertTriggerOid,
19362 updateTriggerOid;
19364 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19365 if (!HeapTupleIsValid(contup))
19366 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19367 conform = (Form_pg_constraint) GETSTRUCT(contup);
19369 /* consider only the inherited foreign keys */
19370 if (conform->contype != CONSTRAINT_FOREIGN ||
19371 !OidIsValid(conform->conparentid))
19373 ReleaseSysCache(contup);
19374 continue;
19377 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19378 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19381 * Also, look up the partition's "check" triggers corresponding to the
19382 * constraint being detached and detach them from the parent triggers.
19384 GetForeignKeyCheckTriggers(trigrel,
19385 fk->conoid, fk->confrelid, fk->conrelid,
19386 &insertTriggerOid, &updateTriggerOid);
19387 Assert(OidIsValid(insertTriggerOid));
19388 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19389 RelationGetRelid(partRel));
19390 Assert(OidIsValid(updateTriggerOid));
19391 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19392 RelationGetRelid(partRel));
19395 * Make the action triggers on the referenced relation. When this was
19396 * a partition the action triggers pointed to the parent rel (they
19397 * still do), but now we need separate ones of our own.
19399 fkconstraint = makeNode(Constraint);
19400 fkconstraint->contype = CONSTRAINT_FOREIGN;
19401 fkconstraint->conname = pstrdup(NameStr(conform->conname));
19402 fkconstraint->deferrable = conform->condeferrable;
19403 fkconstraint->initdeferred = conform->condeferred;
19404 fkconstraint->location = -1;
19405 fkconstraint->pktable = NULL;
19406 fkconstraint->fk_attrs = NIL;
19407 fkconstraint->pk_attrs = NIL;
19408 fkconstraint->fk_matchtype = conform->confmatchtype;
19409 fkconstraint->fk_upd_action = conform->confupdtype;
19410 fkconstraint->fk_del_action = conform->confdeltype;
19411 fkconstraint->fk_del_set_cols = NIL;
19412 fkconstraint->old_conpfeqop = NIL;
19413 fkconstraint->old_pktable_oid = InvalidOid;
19414 fkconstraint->skip_validation = false;
19415 fkconstraint->initially_valid = true;
19417 createForeignKeyActionTriggers(partRel, conform->confrelid,
19418 fkconstraint, fk->conoid,
19419 conform->conindid,
19420 InvalidOid, InvalidOid,
19421 NULL, NULL);
19423 ReleaseSysCache(contup);
19425 list_free_deep(fks);
19426 if (trigrel)
19427 table_close(trigrel, RowExclusiveLock);
19430 * Any sub-constraints that are in the referenced-side of a larger
19431 * constraint have to be removed. This partition is no longer part of the
19432 * key space of the constraint.
19434 foreach(cell, GetParentedForeignKeyRefs(partRel))
19436 Oid constrOid = lfirst_oid(cell);
19437 ObjectAddress constraint;
19439 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19440 deleteDependencyRecordsForClass(ConstraintRelationId,
19441 constrOid,
19442 ConstraintRelationId,
19443 DEPENDENCY_INTERNAL);
19444 CommandCounterIncrement();
19446 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
19447 performDeletion(&constraint, DROP_RESTRICT, 0);
19450 /* Now we can detach indexes */
19451 indexes = RelationGetIndexList(partRel);
19452 foreach(cell, indexes)
19454 Oid idxid = lfirst_oid(cell);
19455 Relation idx;
19456 Oid constrOid;
19458 if (!has_superclass(idxid))
19459 continue;
19461 Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
19462 RelationGetRelid(rel)));
19464 idx = index_open(idxid, AccessExclusiveLock);
19465 IndexSetParentIndex(idx, InvalidOid);
19467 /* If there's a constraint associated with the index, detach it too */
19468 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
19469 idxid);
19470 if (OidIsValid(constrOid))
19471 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19473 index_close(idx, NoLock);
19476 /* Update pg_class tuple */
19477 classRel = table_open(RelationRelationId, RowExclusiveLock);
19478 tuple = SearchSysCacheCopy1(RELOID,
19479 ObjectIdGetDatum(RelationGetRelid(partRel)));
19480 if (!HeapTupleIsValid(tuple))
19481 elog(ERROR, "cache lookup failed for relation %u",
19482 RelationGetRelid(partRel));
19483 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
19485 /* Clear relpartbound and reset relispartition */
19486 memset(new_val, 0, sizeof(new_val));
19487 memset(new_null, false, sizeof(new_null));
19488 memset(new_repl, false, sizeof(new_repl));
19489 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
19490 new_null[Anum_pg_class_relpartbound - 1] = true;
19491 new_repl[Anum_pg_class_relpartbound - 1] = true;
19492 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
19493 new_val, new_null, new_repl);
19495 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
19496 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
19497 heap_freetuple(newtuple);
19498 table_close(classRel, RowExclusiveLock);
19500 if (OidIsValid(defaultPartOid))
19503 * If the relation being detached is the default partition itself,
19504 * remove it from the parent's pg_partitioned_table entry.
19506 * If not, we must invalidate default partition's relcache entry, as
19507 * in StorePartitionBound: its partition constraint depends on every
19508 * other partition's partition constraint.
19510 if (RelationGetRelid(partRel) == defaultPartOid)
19511 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
19512 else
19513 CacheInvalidateRelcacheByRelid(defaultPartOid);
19517 * Invalidate the parent's relcache so that the partition is no longer
19518 * included in its partition descriptor.
19520 CacheInvalidateRelcache(rel);
19523 * If the partition we just detached is partitioned itself, invalidate
19524 * relcache for all descendent partitions too to ensure that their
19525 * rd_partcheck expression trees are rebuilt; must lock partitions before
19526 * doing so, using the same lockmode as what partRel has been locked with
19527 * by the caller.
19529 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19531 List *children;
19533 children = find_all_inheritors(RelationGetRelid(partRel),
19534 AccessExclusiveLock, NULL);
19535 foreach(cell, children)
19537 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
19543 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19545 * To use when a DETACH PARTITION command previously did not run to
19546 * completion; this completes the detaching process.
19548 static ObjectAddress
19549 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
19551 Relation partRel;
19552 ObjectAddress address;
19553 Snapshot snap = GetActiveSnapshot();
19555 partRel = table_openrv(name, AccessExclusiveLock);
19558 * Wait until existing snapshots are gone. This is important if the
19559 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19560 * user could immediately run DETACH FINALIZE without actually waiting for
19561 * existing transactions. We must not complete the detach action until
19562 * all such queries are complete (otherwise we would present them with an
19563 * inconsistent view of catalogs).
19565 WaitForOlderSnapshots(snap->xmin, false);
19567 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
19569 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19571 table_close(partRel, NoLock);
19573 return address;
19577 * DetachAddConstraintIfNeeded
19578 * Subroutine for ATExecDetachPartition. Create a constraint that
19579 * takes the place of the partition constraint, but avoid creating
19580 * a dupe if an constraint already exists which implies the needed
19581 * constraint.
19583 static void
19584 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
19586 List *constraintExpr;
19588 constraintExpr = RelationGetPartitionQual(partRel);
19589 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
19592 * Avoid adding a new constraint if the needed constraint is implied by an
19593 * existing constraint
19595 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
19597 AlteredTableInfo *tab;
19598 Constraint *n;
19600 tab = ATGetQueueEntry(wqueue, partRel);
19602 /* Add constraint on partition, equivalent to the partition constraint */
19603 n = makeNode(Constraint);
19604 n->contype = CONSTR_CHECK;
19605 n->conname = NULL;
19606 n->location = -1;
19607 n->is_no_inherit = false;
19608 n->raw_expr = NULL;
19609 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
19610 n->initially_valid = true;
19611 n->skip_validation = true;
19612 /* It's a re-add, since it nominally already exists */
19613 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
19614 true, false, true, ShareUpdateExclusiveLock);
19619 * DropClonedTriggersFromPartition
19620 * subroutine for ATExecDetachPartition to remove any triggers that were
19621 * cloned to the partition when it was created-as-partition or attached.
19622 * This undoes what CloneRowTriggersToPartition did.
19624 static void
19625 DropClonedTriggersFromPartition(Oid partitionId)
19627 ScanKeyData skey;
19628 SysScanDesc scan;
19629 HeapTuple trigtup;
19630 Relation tgrel;
19631 ObjectAddresses *objects;
19633 objects = new_object_addresses();
19636 * Scan pg_trigger to search for all triggers on this rel.
19638 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19639 F_OIDEQ, ObjectIdGetDatum(partitionId));
19640 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
19641 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
19642 true, NULL, 1, &skey);
19643 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
19645 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
19646 ObjectAddress trig;
19648 /* Ignore triggers that weren't cloned */
19649 if (!OidIsValid(pg_trigger->tgparentid))
19650 continue;
19653 * Ignore internal triggers that are implementation objects of foreign
19654 * keys, because these will be detached when the foreign keys
19655 * themselves are.
19657 if (OidIsValid(pg_trigger->tgconstrrelid))
19658 continue;
19661 * This is ugly, but necessary: remove the dependency markings on the
19662 * trigger so that it can be removed.
19664 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19665 TriggerRelationId,
19666 DEPENDENCY_PARTITION_PRI);
19667 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19668 RelationRelationId,
19669 DEPENDENCY_PARTITION_SEC);
19671 /* remember this trigger to remove it below */
19672 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
19673 add_exact_object_address(&trig, objects);
19676 /* make the dependency removal visible to the deletion below */
19677 CommandCounterIncrement();
19678 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
19680 /* done */
19681 free_object_addresses(objects);
19682 systable_endscan(scan);
19683 table_close(tgrel, RowExclusiveLock);
19687 * Before acquiring lock on an index, acquire the same lock on the owning
19688 * table.
19690 struct AttachIndexCallbackState
19692 Oid partitionOid;
19693 Oid parentTblOid;
19694 bool lockedParentTbl;
19697 static void
19698 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
19699 void *arg)
19701 struct AttachIndexCallbackState *state;
19702 Form_pg_class classform;
19703 HeapTuple tuple;
19705 state = (struct AttachIndexCallbackState *) arg;
19707 if (!state->lockedParentTbl)
19709 LockRelationOid(state->parentTblOid, AccessShareLock);
19710 state->lockedParentTbl = true;
19714 * If we previously locked some other heap, and the name we're looking up
19715 * no longer refers to an index on that relation, release the now-useless
19716 * lock. XXX maybe we should do *after* we verify whether the index does
19717 * not actually belong to the same relation ...
19719 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
19721 UnlockRelationOid(state->partitionOid, AccessShareLock);
19722 state->partitionOid = InvalidOid;
19725 /* Didn't find a relation, so no need for locking or permission checks. */
19726 if (!OidIsValid(relOid))
19727 return;
19729 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
19730 if (!HeapTupleIsValid(tuple))
19731 return; /* concurrently dropped, so nothing to do */
19732 classform = (Form_pg_class) GETSTRUCT(tuple);
19733 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
19734 classform->relkind != RELKIND_INDEX)
19735 ereport(ERROR,
19736 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19737 errmsg("\"%s\" is not an index", rv->relname)));
19738 ReleaseSysCache(tuple);
19741 * Since we need only examine the heap's tupledesc, an access share lock
19742 * on it (preventing any DDL) is sufficient.
19744 state->partitionOid = IndexGetRelation(relOid, false);
19745 LockRelationOid(state->partitionOid, AccessShareLock);
19749 * ALTER INDEX i1 ATTACH PARTITION i2
19751 static ObjectAddress
19752 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
19754 Relation partIdx;
19755 Relation partTbl;
19756 Relation parentTbl;
19757 ObjectAddress address;
19758 Oid partIdxId;
19759 Oid currParent;
19760 struct AttachIndexCallbackState state;
19763 * We need to obtain lock on the index 'name' to modify it, but we also
19764 * need to read its owning table's tuple descriptor -- so we need to lock
19765 * both. To avoid deadlocks, obtain lock on the table before doing so on
19766 * the index. Furthermore, we need to examine the parent table of the
19767 * partition, so lock that one too.
19769 state.partitionOid = InvalidOid;
19770 state.parentTblOid = parentIdx->rd_index->indrelid;
19771 state.lockedParentTbl = false;
19772 partIdxId =
19773 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
19774 RangeVarCallbackForAttachIndex,
19775 (void *) &state);
19776 /* Not there? */
19777 if (!OidIsValid(partIdxId))
19778 ereport(ERROR,
19779 (errcode(ERRCODE_UNDEFINED_OBJECT),
19780 errmsg("index \"%s\" does not exist", name->relname)));
19782 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19783 partIdx = relation_open(partIdxId, AccessExclusiveLock);
19785 /* we already hold locks on both tables, so this is safe: */
19786 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
19787 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19789 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19791 /* Silently do nothing if already in the right state */
19792 currParent = partIdx->rd_rel->relispartition ?
19793 get_partition_parent(partIdxId, false) : InvalidOid;
19794 if (currParent != RelationGetRelid(parentIdx))
19796 IndexInfo *childInfo;
19797 IndexInfo *parentInfo;
19798 AttrMap *attmap;
19799 bool found;
19800 int i;
19801 PartitionDesc partDesc;
19802 Oid constraintOid,
19803 cldConstrId = InvalidOid;
19806 * If this partition already has an index attached, refuse the
19807 * operation.
19809 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19811 if (OidIsValid(currParent))
19812 ereport(ERROR,
19813 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19814 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19815 RelationGetRelationName(partIdx),
19816 RelationGetRelationName(parentIdx)),
19817 errdetail("Index \"%s\" is already attached to another index.",
19818 RelationGetRelationName(partIdx))));
19820 /* Make sure it indexes a partition of the other index's table */
19821 partDesc = RelationGetPartitionDesc(parentTbl, true);
19822 found = false;
19823 for (i = 0; i < partDesc->nparts; i++)
19825 if (partDesc->oids[i] == state.partitionOid)
19827 found = true;
19828 break;
19831 if (!found)
19832 ereport(ERROR,
19833 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19834 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19835 RelationGetRelationName(partIdx),
19836 RelationGetRelationName(parentIdx)),
19837 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19838 RelationGetRelationName(partIdx),
19839 RelationGetRelationName(parentTbl))));
19841 /* Ensure the indexes are compatible */
19842 childInfo = BuildIndexInfo(partIdx);
19843 parentInfo = BuildIndexInfo(parentIdx);
19844 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19845 RelationGetDescr(parentTbl),
19846 false);
19847 if (!CompareIndexInfo(childInfo, parentInfo,
19848 partIdx->rd_indcollation,
19849 parentIdx->rd_indcollation,
19850 partIdx->rd_opfamily,
19851 parentIdx->rd_opfamily,
19852 attmap))
19853 ereport(ERROR,
19854 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19855 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19856 RelationGetRelationName(partIdx),
19857 RelationGetRelationName(parentIdx)),
19858 errdetail("The index definitions do not match.")));
19861 * If there is a constraint in the parent, make sure there is one in
19862 * the child too.
19864 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19865 RelationGetRelid(parentIdx));
19867 if (OidIsValid(constraintOid))
19869 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19870 partIdxId);
19871 if (!OidIsValid(cldConstrId))
19872 ereport(ERROR,
19873 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19874 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19875 RelationGetRelationName(partIdx),
19876 RelationGetRelationName(parentIdx)),
19877 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19878 RelationGetRelationName(parentIdx),
19879 RelationGetRelationName(parentTbl),
19880 RelationGetRelationName(partIdx))));
19884 * If it's a primary key, make sure the columns in the partition are
19885 * NOT NULL.
19887 if (parentIdx->rd_index->indisprimary)
19888 verifyPartitionIndexNotNull(childInfo, partTbl);
19890 /* All good -- do it */
19891 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
19892 if (OidIsValid(constraintOid))
19893 ConstraintSetParentConstraint(cldConstrId, constraintOid,
19894 RelationGetRelid(partTbl));
19896 free_attrmap(attmap);
19898 validatePartitionedIndex(parentIdx, parentTbl);
19901 relation_close(parentTbl, AccessShareLock);
19902 /* keep these locks till commit */
19903 relation_close(partTbl, NoLock);
19904 relation_close(partIdx, NoLock);
19906 return address;
19910 * Verify whether the given partition already contains an index attached
19911 * to the given partitioned index. If so, raise an error.
19913 static void
19914 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
19916 Oid existingIdx;
19918 existingIdx = index_get_partition(partitionTbl,
19919 RelationGetRelid(parentIdx));
19920 if (OidIsValid(existingIdx))
19921 ereport(ERROR,
19922 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19923 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19924 RelationGetRelationName(partIdx),
19925 RelationGetRelationName(parentIdx)),
19926 errdetail("Another index is already attached for partition \"%s\".",
19927 RelationGetRelationName(partitionTbl))));
19931 * Verify whether the set of attached partition indexes to a parent index on
19932 * a partitioned table is complete. If it is, mark the parent index valid.
19934 * This should be called each time a partition index is attached.
19936 static void
19937 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
19939 Relation inheritsRel;
19940 SysScanDesc scan;
19941 ScanKeyData key;
19942 int tuples = 0;
19943 HeapTuple inhTup;
19944 bool updated = false;
19946 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19949 * Scan pg_inherits for this parent index. Count each valid index we find
19950 * (verifying the pg_index entry for each), and if we reach the total
19951 * amount we expect, we can mark this parent index as valid.
19953 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
19954 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
19955 BTEqualStrategyNumber, F_OIDEQ,
19956 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19957 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19958 NULL, 1, &key);
19959 while ((inhTup = systable_getnext(scan)) != NULL)
19961 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19962 HeapTuple indTup;
19963 Form_pg_index indexForm;
19965 indTup = SearchSysCache1(INDEXRELID,
19966 ObjectIdGetDatum(inhForm->inhrelid));
19967 if (!HeapTupleIsValid(indTup))
19968 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
19969 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19970 if (indexForm->indisvalid)
19971 tuples += 1;
19972 ReleaseSysCache(indTup);
19975 /* Done with pg_inherits */
19976 systable_endscan(scan);
19977 table_close(inheritsRel, AccessShareLock);
19980 * If we found as many inherited indexes as the partitioned table has
19981 * partitions, we're good; update pg_index to set indisvalid.
19983 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19985 Relation idxRel;
19986 HeapTuple indTup;
19987 Form_pg_index indexForm;
19989 idxRel = table_open(IndexRelationId, RowExclusiveLock);
19990 indTup = SearchSysCacheCopy1(INDEXRELID,
19991 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19992 if (!HeapTupleIsValid(indTup))
19993 elog(ERROR, "cache lookup failed for index %u",
19994 RelationGetRelid(partedIdx));
19995 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19997 indexForm->indisvalid = true;
19998 updated = true;
20000 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20002 table_close(idxRel, RowExclusiveLock);
20003 heap_freetuple(indTup);
20007 * If this index is in turn a partition of a larger index, validating it
20008 * might cause the parent to become valid also. Try that.
20010 if (updated && partedIdx->rd_rel->relispartition)
20012 Oid parentIdxId,
20013 parentTblId;
20014 Relation parentIdx,
20015 parentTbl;
20017 /* make sure we see the validation we just did */
20018 CommandCounterIncrement();
20020 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20021 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20022 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20023 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20024 Assert(!parentIdx->rd_index->indisvalid);
20026 validatePartitionedIndex(parentIdx, parentTbl);
20028 relation_close(parentIdx, AccessExclusiveLock);
20029 relation_close(parentTbl, AccessExclusiveLock);
20034 * When attaching an index as a partition of a partitioned index which is a
20035 * primary key, verify that all the columns in the partition are marked NOT
20036 * NULL.
20038 static void
20039 verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20041 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20043 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20044 iinfo->ii_IndexAttrNumbers[i] - 1);
20046 if (!att->attnotnull)
20047 ereport(ERROR,
20048 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20049 errmsg("invalid primary key definition"),
20050 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20051 NameStr(att->attname),
20052 RelationGetRelationName(partition)));
20057 * Return an OID list of constraints that reference the given relation
20058 * that are marked as having a parent constraints.
20060 static List *
20061 GetParentedForeignKeyRefs(Relation partition)
20063 Relation pg_constraint;
20064 HeapTuple tuple;
20065 SysScanDesc scan;
20066 ScanKeyData key[2];
20067 List *constraints = NIL;
20070 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20071 * scan.
20073 if (RelationGetIndexList(partition) == NIL ||
20074 bms_is_empty(RelationGetIndexAttrBitmap(partition,
20075 INDEX_ATTR_BITMAP_KEY)))
20076 return NIL;
20078 /* Search for constraints referencing this table */
20079 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20080 ScanKeyInit(&key[0],
20081 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20082 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20083 ScanKeyInit(&key[1],
20084 Anum_pg_constraint_contype, BTEqualStrategyNumber,
20085 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20087 /* XXX This is a seqscan, as we don't have a usable index */
20088 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20089 while ((tuple = systable_getnext(scan)) != NULL)
20091 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20094 * We only need to process constraints that are part of larger ones.
20096 if (!OidIsValid(constrForm->conparentid))
20097 continue;
20099 constraints = lappend_oid(constraints, constrForm->oid);
20102 systable_endscan(scan);
20103 table_close(pg_constraint, AccessShareLock);
20105 return constraints;
20109 * During DETACH PARTITION, verify that any foreign keys pointing to the
20110 * partitioned table would not become invalid. An error is raised if any
20111 * referenced values exist.
20113 static void
20114 ATDetachCheckNoForeignKeyRefs(Relation partition)
20116 List *constraints;
20117 ListCell *cell;
20119 constraints = GetParentedForeignKeyRefs(partition);
20121 foreach(cell, constraints)
20123 Oid constrOid = lfirst_oid(cell);
20124 HeapTuple tuple;
20125 Form_pg_constraint constrForm;
20126 Relation rel;
20127 Trigger trig = {0};
20129 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20130 if (!HeapTupleIsValid(tuple))
20131 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20132 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20134 Assert(OidIsValid(constrForm->conparentid));
20135 Assert(constrForm->confrelid == RelationGetRelid(partition));
20137 /* prevent data changes into the referencing table until commit */
20138 rel = table_open(constrForm->conrelid, ShareLock);
20140 trig.tgoid = InvalidOid;
20141 trig.tgname = NameStr(constrForm->conname);
20142 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20143 trig.tgisinternal = true;
20144 trig.tgconstrrelid = RelationGetRelid(partition);
20145 trig.tgconstrindid = constrForm->conindid;
20146 trig.tgconstraint = constrForm->oid;
20147 trig.tgdeferrable = false;
20148 trig.tginitdeferred = false;
20149 /* we needn't fill in remaining fields */
20151 RI_PartitionRemove_Check(&trig, rel, partition);
20153 ReleaseSysCache(tuple);
20155 table_close(rel, NoLock);
20160 * resolve column compression specification to compression method.
20162 static char
20163 GetAttributeCompression(Oid atttypid, const char *compression)
20165 char cmethod;
20167 if (compression == NULL || strcmp(compression, "default") == 0)
20168 return InvalidCompressionMethod;
20171 * To specify a nondefault method, the column data type must be toastable.
20172 * Note this says nothing about whether the column's attstorage setting
20173 * permits compression; we intentionally allow attstorage and
20174 * attcompression to be independent. But with a non-toastable type,
20175 * attstorage could not be set to a value that would permit compression.
20177 * We don't actually need to enforce this, since nothing bad would happen
20178 * if attcompression were non-default; it would never be consulted. But
20179 * it seems more user-friendly to complain about a certainly-useless
20180 * attempt to set the property.
20182 if (!TypeIsToastable(atttypid))
20183 ereport(ERROR,
20184 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20185 errmsg("column data type %s does not support compression",
20186 format_type_be(atttypid))));
20188 cmethod = CompressionNameToMethod(compression);
20189 if (!CompressionMethodIsValid(cmethod))
20190 ereport(ERROR,
20191 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20192 errmsg("invalid compression method \"%s\"", compression)));
20194 return cmethod;
20198 * resolve column storage specification
20200 static char
20201 GetAttributeStorage(Oid atttypid, const char *storagemode)
20203 char cstorage = 0;
20205 if (pg_strcasecmp(storagemode, "plain") == 0)
20206 cstorage = TYPSTORAGE_PLAIN;
20207 else if (pg_strcasecmp(storagemode, "external") == 0)
20208 cstorage = TYPSTORAGE_EXTERNAL;
20209 else if (pg_strcasecmp(storagemode, "extended") == 0)
20210 cstorage = TYPSTORAGE_EXTENDED;
20211 else if (pg_strcasecmp(storagemode, "main") == 0)
20212 cstorage = TYPSTORAGE_MAIN;
20213 else if (pg_strcasecmp(storagemode, "default") == 0)
20214 cstorage = get_typstorage(atttypid);
20215 else
20216 ereport(ERROR,
20217 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20218 errmsg("invalid storage type \"%s\"",
20219 storagemode)));
20222 * safety check: do not allow toasted storage modes unless column datatype
20223 * is TOAST-aware.
20225 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20226 ereport(ERROR,
20227 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20228 errmsg("column data type %s can only have storage PLAIN",
20229 format_type_be(atttypid))));
20231 return cstorage;