1 /*-------------------------------------------------------------------------
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/gist.h"
20 #include "access/heapam.h"
21 #include "access/heapam_xlog.h"
22 #include "access/multixact.h"
23 #include "access/reloptions.h"
24 #include "access/relscan.h"
25 #include "access/sysattr.h"
26 #include "access/tableam.h"
27 #include "access/toast_compression.h"
28 #include "access/xact.h"
29 #include "access/xlog.h"
30 #include "access/xloginsert.h"
31 #include "catalog/catalog.h"
32 #include "catalog/heap.h"
33 #include "catalog/index.h"
34 #include "catalog/namespace.h"
35 #include "catalog/objectaccess.h"
36 #include "catalog/partition.h"
37 #include "catalog/pg_am.h"
38 #include "catalog/pg_attrdef.h"
39 #include "catalog/pg_collation.h"
40 #include "catalog/pg_constraint.h"
41 #include "catalog/pg_depend.h"
42 #include "catalog/pg_foreign_table.h"
43 #include "catalog/pg_inherits.h"
44 #include "catalog/pg_largeobject.h"
45 #include "catalog/pg_namespace.h"
46 #include "catalog/pg_opclass.h"
47 #include "catalog/pg_policy.h"
48 #include "catalog/pg_proc.h"
49 #include "catalog/pg_publication_rel.h"
50 #include "catalog/pg_rewrite.h"
51 #include "catalog/pg_statistic_ext.h"
52 #include "catalog/pg_tablespace.h"
53 #include "catalog/pg_trigger.h"
54 #include "catalog/pg_type.h"
55 #include "catalog/storage.h"
56 #include "catalog/storage_xlog.h"
57 #include "catalog/toasting.h"
58 #include "commands/cluster.h"
59 #include "commands/comment.h"
60 #include "commands/defrem.h"
61 #include "commands/event_trigger.h"
62 #include "commands/sequence.h"
63 #include "commands/tablecmds.h"
64 #include "commands/tablespace.h"
65 #include "commands/trigger.h"
66 #include "commands/typecmds.h"
67 #include "commands/user.h"
68 #include "commands/vacuum.h"
69 #include "common/int.h"
70 #include "executor/executor.h"
71 #include "foreign/fdwapi.h"
72 #include "foreign/foreign.h"
73 #include "miscadmin.h"
74 #include "nodes/makefuncs.h"
75 #include "nodes/nodeFuncs.h"
76 #include "nodes/parsenodes.h"
77 #include "optimizer/optimizer.h"
78 #include "parser/parse_coerce.h"
79 #include "parser/parse_collate.h"
80 #include "parser/parse_expr.h"
81 #include "parser/parse_relation.h"
82 #include "parser/parse_type.h"
83 #include "parser/parse_utilcmd.h"
84 #include "parser/parser.h"
85 #include "partitioning/partbounds.h"
86 #include "partitioning/partdesc.h"
88 #include "rewrite/rewriteDefine.h"
89 #include "rewrite/rewriteHandler.h"
90 #include "rewrite/rewriteManip.h"
91 #include "storage/bufmgr.h"
92 #include "storage/lmgr.h"
93 #include "storage/lock.h"
94 #include "storage/predicate.h"
95 #include "storage/smgr.h"
96 #include "tcop/utility.h"
97 #include "utils/acl.h"
98 #include "utils/builtins.h"
99 #include "utils/fmgroids.h"
100 #include "utils/inval.h"
101 #include "utils/lsyscache.h"
102 #include "utils/memutils.h"
103 #include "utils/partcache.h"
104 #include "utils/relcache.h"
105 #include "utils/ruleutils.h"
106 #include "utils/snapmgr.h"
107 #include "utils/syscache.h"
108 #include "utils/timestamp.h"
109 #include "utils/typcache.h"
110 #include "utils/usercontext.h"
113 * ON COMMIT action list
115 typedef struct OnCommitItem
117 Oid relid
; /* relid of relation */
118 OnCommitAction oncommit
; /* what to do at end of xact */
121 * If this entry was created during the current transaction,
122 * creating_subid is the ID of the creating subxact; if created in a prior
123 * transaction, creating_subid is zero. If deleted during the current
124 * transaction, deleting_subid is the ID of the deleting subxact; if no
125 * deletion request is pending, deleting_subid is zero.
127 SubTransactionId creating_subid
;
128 SubTransactionId deleting_subid
;
131 static List
*on_commits
= NIL
;
135 * State information for ALTER TABLE
137 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 * structs, one for each table modified by the operation (the named table
139 * plus any child tables that are affected). We save lists of subcommands
140 * to apply to this table (possibly modified by parse transformation steps);
141 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 * necessary information is stored in the constraints and newvals lists.
144 * Phase 2 is divided into multiple passes; subcommands are executed in
145 * a pass determined by subcommand type.
148 typedef enum AlterTablePass
150 AT_PASS_UNSET
= -1, /* UNSET will cause ERROR */
151 AT_PASS_DROP
, /* DROP (all flavors) */
152 AT_PASS_ALTER_TYPE
, /* ALTER COLUMN TYPE */
153 AT_PASS_ADD_COL
, /* ADD COLUMN */
154 AT_PASS_SET_EXPRESSION
, /* ALTER SET EXPRESSION */
155 AT_PASS_OLD_INDEX
, /* re-add existing indexes */
156 AT_PASS_OLD_CONSTR
, /* re-add existing constraints */
157 /* We could support a RENAME COLUMN pass here, but not currently used */
158 AT_PASS_ADD_CONSTR
, /* ADD constraints (initial examination) */
159 AT_PASS_COL_ATTRS
, /* set column attributes, eg NOT NULL */
160 AT_PASS_ADD_INDEXCONSTR
, /* ADD index-based constraints */
161 AT_PASS_ADD_INDEX
, /* ADD indexes */
162 AT_PASS_ADD_OTHERCONSTR
, /* ADD other constraints, defaults */
163 AT_PASS_MISC
, /* other stuff */
166 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
168 typedef struct AlteredTableInfo
170 /* Information saved before any work commences: */
171 Oid relid
; /* Relation to work on */
172 char relkind
; /* Its relkind */
173 TupleDesc oldDesc
; /* Pre-modification tuple descriptor */
176 * Transiently set during Phase 2, normally set to NULL.
178 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 * returns control. This can be exploited by ATExecCmd subroutines to
180 * close/reopen across transaction boundaries.
184 /* Information saved by Phase 1 for Phase 2: */
185 List
*subcmds
[AT_NUM_PASSES
]; /* Lists of AlterTableCmd */
186 /* Information saved by Phases 1/2 for Phase 3: */
187 List
*constraints
; /* List of NewConstraint */
188 List
*newvals
; /* List of NewColumnValue */
189 List
*afterStmts
; /* List of utility command parsetrees */
190 bool verify_new_notnull
; /* T if we should recheck NOT NULL */
191 int rewrite
; /* Reason for forced rewrite, if any */
192 bool chgAccessMethod
; /* T if SET ACCESS METHOD is used */
193 Oid newAccessMethod
; /* new access method; 0 means no change,
194 * if above is true */
195 Oid newTableSpace
; /* new tablespace; 0 means no change */
196 bool chgPersistence
; /* T if SET LOGGED/UNLOGGED is used */
197 char newrelpersistence
; /* if above is true */
198 Expr
*partition_constraint
; /* for attach partition validation */
199 /* true, if validating default due to some other attach/detach */
200 bool validate_default
;
201 /* Objects to rebuild after completing ALTER TYPE operations */
202 List
*changedConstraintOids
; /* OIDs of constraints to rebuild */
203 List
*changedConstraintDefs
; /* string definitions of same */
204 List
*changedIndexOids
; /* OIDs of indexes to rebuild */
205 List
*changedIndexDefs
; /* string definitions of same */
206 char *replicaIdentityIndex
; /* index to reset as REPLICA IDENTITY */
207 char *clusterOnIndex
; /* index to use for CLUSTER */
208 List
*changedStatisticsOids
; /* OIDs of statistics to rebuild */
209 List
*changedStatisticsDefs
; /* string definitions of same */
212 /* Struct describing one new constraint to check in Phase 3 scan */
213 /* Note: new not-null constraints are handled elsewhere */
214 typedef struct NewConstraint
216 char *name
; /* Constraint name, or NULL if none */
217 ConstrType contype
; /* CHECK or FOREIGN */
218 Oid refrelid
; /* PK rel, if FOREIGN */
219 Oid refindid
; /* OID of PK's index, if FOREIGN */
220 bool conwithperiod
; /* Whether the new FOREIGN KEY uses PERIOD */
221 Oid conid
; /* OID of pg_constraint entry, if FOREIGN */
222 Node
*qual
; /* Check expr or CONSTR_FOREIGN Constraint */
223 ExprState
*qualstate
; /* Execution state for CHECK expr */
227 * Struct describing one new column value that needs to be computed during
228 * Phase 3 copy (this could be either a new column with a non-null default, or
229 * a column that we're changing the type of). Columns without such an entry
230 * are just copied from the old table during ATRewriteTable. Note that the
231 * expr is an expression over *old* table values, except when is_generated
232 * is true; then it is an expression over columns of the *new* tuple.
234 typedef struct NewColumnValue
236 AttrNumber attnum
; /* which column */
237 Expr
*expr
; /* expression to compute */
238 ExprState
*exprstate
; /* execution state */
239 bool is_generated
; /* is it a GENERATED expression? */
243 * Error-reporting support for RemoveRelations
245 struct dropmsgstrings
248 int nonexistent_code
;
249 const char *nonexistent_msg
;
250 const char *skipping_msg
;
251 const char *nota_msg
;
252 const char *drophint_msg
;
255 static const struct dropmsgstrings dropmsgstringarray
[] = {
257 ERRCODE_UNDEFINED_TABLE
,
258 gettext_noop("table \"%s\" does not exist"),
259 gettext_noop("table \"%s\" does not exist, skipping"),
260 gettext_noop("\"%s\" is not a table"),
261 gettext_noop("Use DROP TABLE to remove a table.")},
263 ERRCODE_UNDEFINED_TABLE
,
264 gettext_noop("sequence \"%s\" does not exist"),
265 gettext_noop("sequence \"%s\" does not exist, skipping"),
266 gettext_noop("\"%s\" is not a sequence"),
267 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 ERRCODE_UNDEFINED_TABLE
,
270 gettext_noop("view \"%s\" does not exist"),
271 gettext_noop("view \"%s\" does not exist, skipping"),
272 gettext_noop("\"%s\" is not a view"),
273 gettext_noop("Use DROP VIEW to remove a view.")},
275 ERRCODE_UNDEFINED_TABLE
,
276 gettext_noop("materialized view \"%s\" does not exist"),
277 gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 gettext_noop("\"%s\" is not a materialized view"),
279 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 ERRCODE_UNDEFINED_OBJECT
,
282 gettext_noop("index \"%s\" does not exist"),
283 gettext_noop("index \"%s\" does not exist, skipping"),
284 gettext_noop("\"%s\" is not an index"),
285 gettext_noop("Use DROP INDEX to remove an index.")},
286 {RELKIND_COMPOSITE_TYPE
,
287 ERRCODE_UNDEFINED_OBJECT
,
288 gettext_noop("type \"%s\" does not exist"),
289 gettext_noop("type \"%s\" does not exist, skipping"),
290 gettext_noop("\"%s\" is not a type"),
291 gettext_noop("Use DROP TYPE to remove a type.")},
292 {RELKIND_FOREIGN_TABLE
,
293 ERRCODE_UNDEFINED_OBJECT
,
294 gettext_noop("foreign table \"%s\" does not exist"),
295 gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 gettext_noop("\"%s\" is not a foreign table"),
297 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 {RELKIND_PARTITIONED_TABLE
,
299 ERRCODE_UNDEFINED_TABLE
,
300 gettext_noop("table \"%s\" does not exist"),
301 gettext_noop("table \"%s\" does not exist, skipping"),
302 gettext_noop("\"%s\" is not a table"),
303 gettext_noop("Use DROP TABLE to remove a table.")},
304 {RELKIND_PARTITIONED_INDEX
,
305 ERRCODE_UNDEFINED_OBJECT
,
306 gettext_noop("index \"%s\" does not exist"),
307 gettext_noop("index \"%s\" does not exist, skipping"),
308 gettext_noop("\"%s\" is not an index"),
309 gettext_noop("Use DROP INDEX to remove an index.")},
310 {'\0', 0, NULL
, NULL
, NULL
, NULL
}
313 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 struct DropRelationCallbackState
316 /* These fields are set by RemoveRelations: */
317 char expected_relkind
;
318 LOCKMODE heap_lockmode
;
319 /* These fields are state to track which subsidiary locks are held: */
322 /* These fields are passed back by RangeVarCallbackForDropRelation: */
324 char actual_relpersistence
;
327 /* Alter table target-type flags for ATSimplePermissions */
328 #define ATT_TABLE 0x0001
329 #define ATT_VIEW 0x0002
330 #define ATT_MATVIEW 0x0004
331 #define ATT_INDEX 0x0008
332 #define ATT_COMPOSITE_TYPE 0x0010
333 #define ATT_FOREIGN_TABLE 0x0020
334 #define ATT_PARTITIONED_INDEX 0x0040
335 #define ATT_SEQUENCE 0x0080
336 #define ATT_PARTITIONED_TABLE 0x0100
339 * ForeignTruncateInfo
341 * Information related to truncation of foreign tables. This is used for
342 * the elements in a hash table. It uses the server OID as lookup key,
343 * and includes a per-server list of all foreign tables involved in the
346 typedef struct ForeignTruncateInfo
350 } ForeignTruncateInfo
;
352 /* Partial or complete FK creation in addFkConstraint() */
353 typedef enum addFkConstraintSides
356 addFkReferencingSide
,
358 } addFkConstraintSides
;
361 * Partition tables are expected to be dropped when the parent partitioned
362 * table gets dropped. Hence for partitioning we use AUTO dependency.
363 * Otherwise, for regular inheritance use NORMAL dependency.
365 #define child_dependency_type(child_is_partition) \
366 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368 static void truncate_check_rel(Oid relid
, Form_pg_class reltuple
);
369 static void truncate_check_perms(Oid relid
, Form_pg_class reltuple
);
370 static void truncate_check_activity(Relation rel
);
371 static void RangeVarCallbackForTruncate(const RangeVar
*relation
,
372 Oid relId
, Oid oldRelId
, void *arg
);
373 static List
*MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
374 bool is_partition
, List
**supconstr
,
376 static List
*MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
, bool is_enforced
);
377 static void MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
);
378 static ColumnDef
*MergeInheritedAttribute(List
*inh_columns
, int exist_attno
, const ColumnDef
*newdef
);
379 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
);
380 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
381 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
382 bool child_is_partition
);
383 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
384 int32 seqNumber
, Relation inhRelation
,
385 bool child_is_partition
);
386 static int findAttrByName(const char *attributeName
, const List
*columns
);
387 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
388 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
389 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
390 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
392 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
393 bool recurse
, bool recursing
, LOCKMODE lockmode
);
394 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
395 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
397 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
398 Relation rel
, char *constrName
,
399 bool recurse
, bool recursing
, LOCKMODE lockmode
);
400 static int transformColumnNameList(Oid relId
, List
*colList
,
401 int16
*attnums
, Oid
*atttypids
, Oid
*attcollids
);
402 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
404 int16
*attnums
, Oid
*atttypids
, Oid
*attcollids
,
405 Oid
*opclasses
, bool *pk_has_without_overlaps
);
406 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
407 int numattrs
, int16
*attnums
,
408 bool with_period
, Oid
*opclasses
,
409 bool *pk_has_without_overlaps
);
410 static void checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
);
411 static CoercionPathType
findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
,
413 static void validateForeignKeyConstraint(char *conname
,
414 Relation rel
, Relation pkrel
,
415 Oid pkindOid
, Oid constraintOid
, bool hasperiod
);
416 static void CheckAlterTableIsSafe(Relation rel
);
417 static void ATController(AlterTableStmt
*parsetree
,
418 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
419 AlterTableUtilityContext
*context
);
420 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
421 bool recurse
, bool recursing
, LOCKMODE lockmode
,
422 AlterTableUtilityContext
*context
);
423 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
424 AlterTableUtilityContext
*context
);
425 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
426 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
427 AlterTableUtilityContext
*context
);
428 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
429 Relation rel
, AlterTableCmd
*cmd
,
430 bool recurse
, LOCKMODE lockmode
,
431 AlterTablePass cur_pass
,
432 AlterTableUtilityContext
*context
);
433 static void ATRewriteTables(AlterTableStmt
*parsetree
,
434 List
**wqueue
, LOCKMODE lockmode
,
435 AlterTableUtilityContext
*context
);
436 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
);
437 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
438 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
439 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
440 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
441 AlterTableUtilityContext
*context
);
442 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
443 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
445 AlterTableUtilityContext
*context
);
446 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
447 DropBehavior behavior
);
448 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
449 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
450 AlterTableUtilityContext
*context
);
451 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
452 Relation rel
, AlterTableCmd
**cmd
,
453 bool recurse
, bool recursing
,
454 LOCKMODE lockmode
, AlterTablePass cur_pass
,
455 AlterTableUtilityContext
*context
);
456 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
458 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
459 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
460 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
462 static void set_attnotnull(List
**wqueue
, Relation rel
, AttrNumber attnum
,
464 static ObjectAddress
ATExecSetNotNull(List
**wqueue
, Relation rel
,
465 char *constrname
, char *colName
,
466 bool recurse
, bool recursing
,
468 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
469 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
470 List
*testConstraint
, List
*provenConstraint
);
471 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
472 Node
*newDefault
, LOCKMODE lockmode
);
473 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
475 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
476 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
477 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
478 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
479 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
480 bool recurse
, bool recursing
);
481 static ObjectAddress
ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
482 Node
*newExpr
, LOCKMODE lockmode
);
483 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
484 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
485 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
486 Node
*newValue
, LOCKMODE lockmode
);
487 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
488 Node
*options
, bool isReset
, LOCKMODE lockmode
);
489 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
490 Node
*newValue
, LOCKMODE lockmode
);
491 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
492 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
493 AlterTableUtilityContext
*context
);
494 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
495 DropBehavior behavior
,
496 bool recurse
, bool recursing
,
497 bool missing_ok
, LOCKMODE lockmode
,
498 ObjectAddresses
*addrs
);
499 static void ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
500 bool recurse
, LOCKMODE lockmode
,
501 AlterTableUtilityContext
*context
);
502 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
503 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
504 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
505 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
506 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
507 AlteredTableInfo
*tab
, Relation rel
,
508 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
510 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
511 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
512 IndexStmt
*stmt
, LOCKMODE lockmode
);
513 static ObjectAddress
ATAddCheckNNConstraint(List
**wqueue
,
514 AlteredTableInfo
*tab
, Relation rel
,
516 bool recurse
, bool recursing
, bool is_readd
,
518 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
519 Relation rel
, Constraint
*fkconstraint
,
520 bool recurse
, bool recursing
,
522 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
523 int numfksetcols
, const int16
*fksetcolsattnums
,
525 static ObjectAddress
addFkConstraint(addFkConstraintSides fkside
,
526 char *constraintname
,
527 Constraint
*fkconstraint
, Relation rel
,
528 Relation pkrel
, Oid indexOid
,
530 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
531 Oid
*pfeqoperators
, Oid
*ppeqoperators
,
532 Oid
*ffeqoperators
, int numfkdelsetcols
,
533 int16
*fkdelsetcols
, bool is_internal
,
535 static void addFkRecurseReferenced(Constraint
*fkconstraint
,
536 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
537 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
538 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
539 int numfkdelsetcols
, int16
*fkdelsetcols
,
541 Oid parentDelTrigger
, Oid parentUpdTrigger
,
543 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
544 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
545 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
546 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
547 int numfkdelsetcols
, int16
*fkdelsetcols
,
548 bool old_check_ok
, LOCKMODE lockmode
,
549 Oid parentInsTrigger
, Oid parentUpdTrigger
,
551 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
552 Relation partitionRel
);
553 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
554 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
556 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
557 Constraint
*fkconstraint
, Oid constraintOid
,
559 Oid parentInsTrigger
, Oid parentUpdTrigger
,
560 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
561 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
562 Constraint
*fkconstraint
, Oid constraintOid
,
564 Oid parentDelTrigger
, Oid parentUpdTrigger
,
565 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
566 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
568 Oid parentConstrOid
, int numfks
,
569 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
571 Oid parentInsTrigger
,
572 Oid parentUpdTrigger
,
574 static void GetForeignKeyActionTriggers(Relation trigrel
,
575 Oid conoid
, Oid confrelid
, Oid conrelid
,
576 Oid
*deleteTriggerOid
,
577 Oid
*updateTriggerOid
);
578 static void GetForeignKeyCheckTriggers(Relation trigrel
,
579 Oid conoid
, Oid confrelid
, Oid conrelid
,
580 Oid
*insertTriggerOid
,
581 Oid
*updateTriggerOid
);
582 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
583 DropBehavior behavior
, bool recurse
,
584 bool missing_ok
, LOCKMODE lockmode
);
585 static ObjectAddress
dropconstraint_internal(Relation rel
,
586 HeapTuple constraintTup
, DropBehavior behavior
,
587 bool recurse
, bool recursing
,
588 bool missing_ok
, LOCKMODE lockmode
);
589 static void ATPrepAlterColumnType(List
**wqueue
,
590 AlteredTableInfo
*tab
, Relation rel
,
591 bool recurse
, bool recursing
,
592 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
593 AlterTableUtilityContext
*context
);
594 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
595 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
596 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
597 static void RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
598 Relation rel
, AttrNumber attnum
, const char *colName
);
599 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
600 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
601 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
602 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
604 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
605 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
607 static void RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
,
608 Oid objid
, Relation rel
, List
*domname
,
609 const char *conname
);
610 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
611 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
612 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
613 List
*options
, LOCKMODE lockmode
);
614 static void change_owner_fix_column_acls(Oid relationOid
,
615 Oid oldOwnerId
, Oid newOwnerId
);
616 static void change_owner_recurse_to_sequences(Oid relationOid
,
617 Oid newOwnerId
, LOCKMODE lockmode
);
618 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
620 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
621 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
622 static void ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
);
623 static void ATPrepChangePersistence(AlteredTableInfo
*tab
, Relation rel
,
625 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
626 const char *tablespacename
, LOCKMODE lockmode
);
627 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
628 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
629 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
630 AlterTableType operation
,
632 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
633 char fires_when
, bool skip_system
, bool recurse
,
635 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
636 char fires_when
, LOCKMODE lockmode
);
637 static void ATPrepAddInherit(Relation child_rel
);
638 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
639 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
640 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
641 DependencyType deptype
);
642 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
643 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
644 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
645 static void ATExecGenericOptions(Relation rel
, List
*options
);
646 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
647 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
648 static ObjectAddress
ATExecSetCompression(Relation rel
,
649 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
651 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
652 static const char *storage_name(char c
);
654 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
655 Oid oldRelOid
, void *arg
);
656 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
657 Oid oldrelid
, void *arg
);
658 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
659 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
660 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
661 PartitionStrategy strategy
);
662 static void CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
);
663 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
664 bool expect_detached
);
665 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
667 AlterTableUtilityContext
*context
);
668 static void AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
);
669 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
670 List
*partConstraint
,
671 bool validate_default
);
672 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
673 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
674 static void DropClonedTriggersFromPartition(Oid partitionId
);
675 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
676 Relation rel
, RangeVar
*name
,
678 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
679 bool concurrent
, Oid defaultPartOid
);
680 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
681 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
683 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
684 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
685 Relation partitionTbl
);
686 static void verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partIdx
);
687 static List
*GetParentedForeignKeyRefs(Relation partition
);
688 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
689 static char GetAttributeCompression(Oid atttypid
, const char *compression
);
690 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
693 /* ----------------------------------------------------------------
695 * Creates a new relation.
697 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
698 * The other arguments are used to extend the behavior for other cases:
699 * relkind: relkind to assign to the new relation
700 * ownerId: if not InvalidOid, use this as the new relation's owner.
701 * typaddress: if not null, it's set to the pg_type entry's address.
702 * queryString: for error reporting
704 * Note that permissions checks are done against current user regardless of
705 * ownerId. A nonzero ownerId is used when someone is creating a relation
706 * "on behalf of" someone else, so we still want to see that the current user
707 * has permissions to do it.
709 * If successful, returns the address of the new relation.
710 * ----------------------------------------------------------------
713 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
714 ObjectAddress
*typaddress
, const char *queryString
)
716 char relname
[NAMEDATALEN
];
721 TupleDesc descriptor
;
723 List
*old_constraints
;
726 List
*cookedDefaults
;
732 const char *const validnsps
[] = HEAP_RELOPT_NAMESPACES
;
734 ObjectAddress address
;
735 LOCKMODE parentLockmode
;
736 Oid accessMethodId
= InvalidOid
;
739 * Truncate relname to appropriate length (probably a waste of time, as
740 * parser should have done this already).
742 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
745 * Check consistency of arguments
747 if (stmt
->oncommit
!= ONCOMMIT_NOOP
748 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
750 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
751 errmsg("ON COMMIT can only be used on temporary tables")));
753 if (stmt
->partspec
!= NULL
)
755 if (relkind
!= RELKIND_RELATION
)
756 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
758 relkind
= RELKIND_PARTITIONED_TABLE
;
764 if (relkind
== RELKIND_PARTITIONED_TABLE
&&
765 stmt
->relation
->relpersistence
== RELPERSISTENCE_UNLOGGED
)
767 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
768 errmsg("partitioned tables cannot be unlogged")));
771 * Look up the namespace in which we are supposed to create the relation,
772 * check we have permission to create there, lock it against concurrent
773 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
774 * namespace is selected.
777 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
780 * Security check: disallow creating temp tables from security-restricted
781 * code. This is needed because calling code might not expect untrusted
782 * tables to appear in pg_temp at the front of its search path.
784 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
785 && InSecurityRestrictedOperation())
787 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
788 errmsg("cannot create temporary table within security-restricted operation")));
791 * Determine the lockmode to use when scanning parents. A self-exclusive
792 * lock is needed here.
794 * For regular inheritance, if two backends attempt to add children to the
795 * same parent simultaneously, and that parent has no pre-existing
796 * children, then both will attempt to update the parent's relhassubclass
797 * field, leading to a "tuple concurrently updated" error. Also, this
798 * interlocks against a concurrent ANALYZE on the parent table, which
799 * might otherwise be attempting to clear the parent's relhassubclass
800 * field, if its previous children were recently dropped.
802 * If the child table is a partition, then we instead grab an exclusive
803 * lock on the parent because its partition descriptor will be changed by
804 * addition of the new partition.
806 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
807 ShareUpdateExclusiveLock
);
809 /* Determine the list of OIDs of the parents. */
811 foreach(listptr
, stmt
->inhRelations
)
813 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
816 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
819 * Reject duplications in the list of parents.
821 if (list_member_oid(inheritOids
, parentOid
))
823 (errcode(ERRCODE_DUPLICATE_TABLE
),
824 errmsg("relation \"%s\" would be inherited from more than once",
825 get_rel_name(parentOid
))));
827 inheritOids
= lappend_oid(inheritOids
, parentOid
);
831 * Select tablespace to use: an explicitly indicated one, or (in the case
832 * of a partitioned table) the parent's, if it has one.
834 if (stmt
->tablespacename
)
836 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
838 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
840 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
841 errmsg("cannot specify default tablespace for partitioned relations")));
843 else if (stmt
->partbound
)
845 Assert(list_length(inheritOids
) == 1);
846 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
849 tablespaceId
= InvalidOid
;
851 /* still nothing? use the default */
852 if (!OidIsValid(tablespaceId
))
853 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
856 /* Check permissions except when using database's default */
857 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
861 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
863 if (aclresult
!= ACLCHECK_OK
)
864 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
865 get_tablespace_name(tablespaceId
));
868 /* In all cases disallow placing user relations in pg_global */
869 if (tablespaceId
== GLOBALTABLESPACE_OID
)
871 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
872 errmsg("only shared relations can be placed in pg_global tablespace")));
874 /* Identify user ID that will own the table */
875 if (!OidIsValid(ownerId
))
876 ownerId
= GetUserId();
879 * Parse and validate reloptions, if any.
881 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
887 (void) view_reloptions(reloptions
, true);
889 case RELKIND_PARTITIONED_TABLE
:
890 (void) partitioned_table_reloptions(reloptions
, true);
893 (void) heap_reloptions(relkind
, reloptions
, true);
896 if (stmt
->ofTypename
)
900 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
902 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
903 if (aclresult
!= ACLCHECK_OK
)
904 aclcheck_error_type(aclresult
, ofTypeId
);
907 ofTypeId
= InvalidOid
;
910 * Look up inheritance ancestors and generate relation schema, including
911 * inherited attributes. (Note that stmt->tableElts is destructively
912 * modified by MergeAttributes.)
915 MergeAttributes(stmt
->tableElts
, inheritOids
,
916 stmt
->relation
->relpersistence
,
917 stmt
->partbound
!= NULL
,
918 &old_constraints
, &old_notnulls
);
921 * Create a tuple descriptor from the relation schema. Note that this
922 * deals with column names, types, and in-descriptor NOT NULL flags, but
923 * not default values, NOT NULL or CHECK constraints; we handle those
926 descriptor
= BuildDescForRelation(stmt
->tableElts
);
929 * Find columns with default values and prepare for insertion of the
930 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
931 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
932 * while raw defaults go into a list of RawColumnDefault structs that will
933 * be processed by AddRelationNewConstraints. (We can't deal with raw
934 * expressions until we can do transformExpr.)
936 * We can set the atthasdef flags now in the tuple descriptor; this just
937 * saves StoreAttrDefault from having to do an immediate update of the
941 cookedDefaults
= NIL
;
944 foreach(listptr
, stmt
->tableElts
)
946 ColumnDef
*colDef
= lfirst(listptr
);
947 Form_pg_attribute attr
;
950 attr
= TupleDescAttr(descriptor
, attnum
- 1);
952 if (colDef
->raw_default
!= NULL
)
954 RawColumnDefault
*rawEnt
;
956 Assert(colDef
->cooked_default
== NULL
);
958 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
959 rawEnt
->attnum
= attnum
;
960 rawEnt
->raw_default
= colDef
->raw_default
;
961 rawEnt
->missingMode
= false;
962 rawEnt
->generated
= colDef
->generated
;
963 rawDefaults
= lappend(rawDefaults
, rawEnt
);
964 attr
->atthasdef
= true;
966 else if (colDef
->cooked_default
!= NULL
)
968 CookedConstraint
*cooked
;
970 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
971 cooked
->contype
= CONSTR_DEFAULT
;
972 cooked
->conoid
= InvalidOid
; /* until created */
974 cooked
->attnum
= attnum
;
975 cooked
->expr
= colDef
->cooked_default
;
976 cooked
->is_enforced
= true;
977 cooked
->skip_validation
= false;
978 cooked
->is_local
= true; /* not used for defaults */
979 cooked
->inhcount
= 0; /* ditto */
980 cooked
->is_no_inherit
= false;
981 cookedDefaults
= lappend(cookedDefaults
, cooked
);
982 attr
->atthasdef
= true;
985 populate_compact_attribute(descriptor
, attnum
- 1);
989 * For relations with table AM and partitioned tables, select access
990 * method to use: an explicitly indicated one, or (in the case of a
991 * partitioned table) the parent's, if it has one.
993 if (stmt
->accessMethod
!= NULL
)
995 Assert(RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
);
996 accessMethodId
= get_table_am_oid(stmt
->accessMethod
, false);
998 else if (RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
)
1000 if (stmt
->partbound
)
1002 Assert(list_length(inheritOids
) == 1);
1003 accessMethodId
= get_rel_relam(linitial_oid(inheritOids
));
1006 if (RELKIND_HAS_TABLE_AM(relkind
) && !OidIsValid(accessMethodId
))
1007 accessMethodId
= get_table_am_oid(default_table_access_method
, false);
1011 * Create the relation. Inherited defaults and CHECK constraints are
1012 * passed in for immediate handling --- since they don't need parsing,
1013 * they can be stored immediately.
1015 relationId
= heap_create_with_catalog(relname
,
1024 list_concat(cookedDefaults
,
1027 stmt
->relation
->relpersistence
,
1033 allowSystemTableMods
,
1039 * We must bump the command counter to make the newly-created relation
1040 * tuple visible for opening.
1042 CommandCounterIncrement();
1045 * Open the new relation and acquire exclusive lock on it. This isn't
1046 * really necessary for locking out other backends (since they can't see
1047 * the new rel anyway until we commit), but it keeps the lock manager from
1048 * complaining about deadlock risks.
1050 rel
= relation_open(relationId
, AccessExclusiveLock
);
1053 * Now add any newly specified column default and generation expressions
1054 * to the new relation. These are passed to us in the form of raw
1055 * parsetrees; we need to transform them to executable expression trees
1056 * before they can be added. The most convenient way to do that is to
1057 * apply the parser's transformExpr routine, but transformExpr doesn't
1058 * work unless we have a pre-existing relation. So, the transformation has
1059 * to be postponed to this final step of CREATE TABLE.
1061 * This needs to be before processing the partitioning clauses because
1062 * those could refer to generated columns.
1065 AddRelationNewConstraints(rel
, rawDefaults
, NIL
,
1066 true, true, false, queryString
);
1069 * Make column generation expressions visible for use by partitioning.
1071 CommandCounterIncrement();
1073 /* Process and store partition bound, if any. */
1074 if (stmt
->partbound
)
1076 PartitionBoundSpec
*bound
;
1078 Oid parentId
= linitial_oid(inheritOids
),
1082 ParseNamespaceItem
*nsitem
;
1084 /* Already have strong enough lock on the parent */
1085 parent
= table_open(parentId
, NoLock
);
1088 * We are going to try to validate the partition bound specification
1089 * against the partition key of parentRel, so it better have one.
1091 if (parent
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
1093 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1094 errmsg("\"%s\" is not partitioned",
1095 RelationGetRelationName(parent
))));
1098 * The partition constraint of the default partition depends on the
1099 * partition bounds of every other partition. It is possible that
1100 * another backend might be about to execute a query on the default
1101 * partition table, and that the query relies on previously cached
1102 * default partition constraints. We must therefore take a table lock
1103 * strong enough to prevent all queries on the default partition from
1104 * proceeding until we commit and send out a shared-cache-inval notice
1105 * that will make them update their index lists.
1107 * Order of locking: The relation being added won't be visible to
1108 * other backends until it is committed, hence here in
1109 * DefineRelation() the order of locking the default partition and the
1110 * relation being added does not matter. But at all other places we
1111 * need to lock the default relation before we lock the relation being
1112 * added or removed i.e. we should take the lock in same order at all
1113 * the places such that lock parent, lock default partition and then
1114 * lock the partition so as to avoid a deadlock.
1117 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent
,
1119 if (OidIsValid(defaultPartOid
))
1120 defaultRel
= table_open(defaultPartOid
, AccessExclusiveLock
);
1122 /* Transform the bound values */
1123 pstate
= make_parsestate(NULL
);
1124 pstate
->p_sourcetext
= queryString
;
1127 * Add an nsitem containing this relation, so that transformExpr
1128 * called on partition bound expressions is able to report errors
1129 * using a proper context.
1131 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
1132 NULL
, false, false);
1133 addNSItemToQuery(pstate
, nsitem
, false, true, true);
1135 bound
= transformPartitionBound(pstate
, parent
, stmt
->partbound
);
1138 * Check first that the new partition's bound is valid and does not
1139 * overlap with any of existing partitions of the parent.
1141 check_new_partition_bound(relname
, parent
, bound
, pstate
);
1144 * If the default partition exists, its partition constraints will
1145 * change after the addition of this new partition such that it won't
1146 * allow any row that qualifies for this new partition. So, check that
1147 * the existing data in the default partition satisfies the constraint
1148 * as it will exist after adding this partition.
1150 if (OidIsValid(defaultPartOid
))
1152 check_default_partition_contents(parent
, defaultRel
, bound
);
1153 /* Keep the lock until commit. */
1154 table_close(defaultRel
, NoLock
);
1157 /* Update the pg_class entry. */
1158 StorePartitionBound(rel
, parent
, bound
);
1160 table_close(parent
, NoLock
);
1163 /* Store inheritance information for new rel. */
1164 StoreCatalogInheritance(relationId
, inheritOids
, stmt
->partbound
!= NULL
);
1167 * Process the partitioning specification (if any) and store the partition
1168 * key information into the catalog.
1174 AttrNumber partattrs
[PARTITION_MAX_KEYS
];
1175 Oid partopclass
[PARTITION_MAX_KEYS
];
1176 Oid partcollation
[PARTITION_MAX_KEYS
];
1177 List
*partexprs
= NIL
;
1179 pstate
= make_parsestate(NULL
);
1180 pstate
->p_sourcetext
= queryString
;
1182 partnatts
= list_length(stmt
->partspec
->partParams
);
1184 /* Protect fixed-size arrays here and in executor */
1185 if (partnatts
> PARTITION_MAX_KEYS
)
1187 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
1188 errmsg("cannot partition using more than %d columns",
1189 PARTITION_MAX_KEYS
)));
1192 * We need to transform the raw parsetrees corresponding to partition
1193 * expressions into executable expression trees. Like column defaults
1194 * and CHECK constraints, we could not have done the transformation
1197 stmt
->partspec
= transformPartitionSpec(rel
, stmt
->partspec
);
1199 ComputePartitionAttrs(pstate
, rel
, stmt
->partspec
->partParams
,
1200 partattrs
, &partexprs
, partopclass
,
1201 partcollation
, stmt
->partspec
->strategy
);
1203 StorePartitionKey(rel
, stmt
->partspec
->strategy
, partnatts
, partattrs
,
1205 partopclass
, partcollation
);
1207 /* make it all visible */
1208 CommandCounterIncrement();
1212 * If we're creating a partition, create now all the indexes, triggers,
1213 * FKs defined in the parent.
1215 * We can't do it earlier, because DefineIndex wants to know the partition
1216 * key which we just stored.
1218 if (stmt
->partbound
)
1220 Oid parentId
= linitial_oid(inheritOids
);
1225 /* Already have strong enough lock on the parent */
1226 parent
= table_open(parentId
, NoLock
);
1227 idxlist
= RelationGetIndexList(parent
);
1230 * For each index in the parent table, create one in the partition
1232 foreach(cell
, idxlist
)
1234 Relation idxRel
= index_open(lfirst_oid(cell
), AccessShareLock
);
1239 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1241 if (idxRel
->rd_index
->indisunique
)
1243 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1244 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1245 RelationGetRelationName(parent
)),
1246 errdetail("Table \"%s\" contains indexes that are unique.",
1247 RelationGetRelationName(parent
))));
1250 index_close(idxRel
, AccessShareLock
);
1255 attmap
= build_attrmap_by_name(RelationGetDescr(rel
),
1256 RelationGetDescr(parent
),
1259 generateClonedIndexStmt(NULL
, idxRel
,
1260 attmap
, &constraintOid
);
1261 DefineIndex(RelationGetRelid(rel
),
1264 RelationGetRelid(idxRel
),
1267 false, false, false, false, false);
1269 index_close(idxRel
, AccessShareLock
);
1275 * If there are any row-level triggers, clone them to the new
1278 if (parent
->trigdesc
!= NULL
)
1279 CloneRowTriggersToPartition(parent
, rel
);
1282 * And foreign keys too. Note that because we're freshly creating the
1283 * table, there is no need to verify these new constraints.
1285 CloneForeignKeyConstraints(NULL
, parent
, rel
);
1287 table_close(parent
, NoLock
);
1291 * Now add any newly specified CHECK constraints to the new relation. Same
1292 * as for defaults above, but these need to come after partitioning is set
1295 if (stmt
->constraints
)
1296 AddRelationNewConstraints(rel
, NIL
, stmt
->constraints
,
1297 true, true, false, queryString
);
1300 * Finally, merge the not-null constraints that are declared directly with
1301 * those that come from parent relations (making sure to count inheritance
1302 * appropriately for each), create them, and set the attnotnull flag on
1303 * columns that don't yet have it.
1305 nncols
= AddRelationNotNullConstraints(rel
, stmt
->nnconstraints
,
1307 foreach_int(attrnum
, nncols
)
1308 set_attnotnull(NULL
, rel
, attrnum
, NoLock
);
1310 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1313 * Clean up. We keep lock on new relation (although it shouldn't be
1314 * visible to anyone else anyway, until commit).
1316 relation_close(rel
, NoLock
);
1322 * BuildDescForRelation
1324 * Given a list of ColumnDef nodes, build a TupleDesc.
1326 * Note: This is only for the limited purpose of table and view creation. Not
1327 * everything is filled in. A real tuple descriptor should be obtained from
1331 BuildDescForRelation(const List
*columns
)
1344 * allocate a new tuple descriptor
1346 natts
= list_length(columns
);
1347 desc
= CreateTemplateTupleDesc(natts
);
1353 ColumnDef
*entry
= lfirst(l
);
1354 AclResult aclresult
;
1355 Form_pg_attribute att
;
1358 * for each entry in the list, get the name and type information from
1359 * the list and have TupleDescInitEntry fill in the attribute
1360 * information we need.
1364 attname
= entry
->colname
;
1365 typenameTypeIdAndMod(NULL
, entry
->typeName
, &atttypid
, &atttypmod
);
1367 aclresult
= object_aclcheck(TypeRelationId
, atttypid
, GetUserId(), ACL_USAGE
);
1368 if (aclresult
!= ACLCHECK_OK
)
1369 aclcheck_error_type(aclresult
, atttypid
);
1371 attcollation
= GetColumnDefCollation(NULL
, entry
, atttypid
);
1372 attdim
= list_length(entry
->typeName
->arrayBounds
);
1373 if (attdim
> PG_INT16_MAX
)
1375 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1376 errmsg("too many array dimensions"));
1378 if (entry
->typeName
->setof
)
1380 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
1381 errmsg("column \"%s\" cannot be declared SETOF",
1384 TupleDescInitEntry(desc
, attnum
, attname
,
1385 atttypid
, atttypmod
, attdim
);
1386 att
= TupleDescAttr(desc
, attnum
- 1);
1388 /* Override TupleDescInitEntry's settings as requested */
1389 TupleDescInitEntryCollation(desc
, attnum
, attcollation
);
1391 /* Fill in additional stuff not handled by TupleDescInitEntry */
1392 att
->attnotnull
= entry
->is_not_null
;
1393 att
->attislocal
= entry
->is_local
;
1394 att
->attinhcount
= entry
->inhcount
;
1395 att
->attidentity
= entry
->identity
;
1396 att
->attgenerated
= entry
->generated
;
1397 att
->attcompression
= GetAttributeCompression(att
->atttypid
, entry
->compression
);
1399 att
->attstorage
= entry
->storage
;
1400 else if (entry
->storage_name
)
1401 att
->attstorage
= GetAttributeStorage(att
->atttypid
, entry
->storage_name
);
1403 populate_compact_attribute(desc
, attnum
- 1);
1410 * Emit the right error or warning message for a "DROP" command issued on a
1411 * non-existent relation
1414 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1416 const struct dropmsgstrings
*rentry
;
1418 if (rel
->schemaname
!= NULL
&&
1419 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1424 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1425 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1430 (errmsg("schema \"%s\" does not exist, skipping",
1436 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1438 if (rentry
->kind
== rightkind
)
1443 (errcode(rentry
->nonexistent_code
),
1444 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1448 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1454 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1458 * Emit the right error message for a "DROP" command issued on a
1459 * relation of the wrong type
1462 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1464 const struct dropmsgstrings
*rentry
;
1465 const struct dropmsgstrings
*wentry
;
1467 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1468 if (rentry
->kind
== rightkind
)
1470 Assert(rentry
->kind
!= '\0');
1472 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1473 if (wentry
->kind
== wrongkind
)
1475 /* wrongkind could be something we don't have in our table... */
1478 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1479 errmsg(rentry
->nota_msg
, relname
),
1480 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1485 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1486 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1489 RemoveRelations(DropStmt
*drop
)
1491 ObjectAddresses
*objects
;
1495 LOCKMODE lockmode
= AccessExclusiveLock
;
1497 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1498 if (drop
->concurrent
)
1501 * Note that for temporary relations this lock may get upgraded later
1502 * on, but as no other session can access a temporary relation, this
1505 lockmode
= ShareUpdateExclusiveLock
;
1506 Assert(drop
->removeType
== OBJECT_INDEX
);
1507 if (list_length(drop
->objects
) != 1)
1509 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1510 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1511 if (drop
->behavior
== DROP_CASCADE
)
1513 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1514 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1518 * First we identify all the relations, then we delete them in a single
1519 * performMultipleDeletions() call. This is to avoid unwanted DROP
1520 * RESTRICT errors if one of the relations depends on another.
1523 /* Determine required relkind */
1524 switch (drop
->removeType
)
1527 relkind
= RELKIND_RELATION
;
1531 relkind
= RELKIND_INDEX
;
1534 case OBJECT_SEQUENCE
:
1535 relkind
= RELKIND_SEQUENCE
;
1539 relkind
= RELKIND_VIEW
;
1542 case OBJECT_MATVIEW
:
1543 relkind
= RELKIND_MATVIEW
;
1546 case OBJECT_FOREIGN_TABLE
:
1547 relkind
= RELKIND_FOREIGN_TABLE
;
1551 elog(ERROR
, "unrecognized drop object type: %d",
1552 (int) drop
->removeType
);
1553 relkind
= 0; /* keep compiler quiet */
1557 /* Lock and validate each relation; build a list of object addresses */
1558 objects
= new_object_addresses();
1560 foreach(cell
, drop
->objects
)
1562 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1565 struct DropRelationCallbackState state
;
1568 * These next few steps are a great deal like relation_openrv, but we
1569 * don't bother building a relcache entry since we don't need it.
1571 * Check for shared-cache-inval messages before trying to access the
1572 * relation. This is needed to cover the case where the name
1573 * identifies a rel that has been dropped and recreated since the
1574 * start of our transaction: if we don't flush the old syscache entry,
1575 * then we'll latch onto that entry and suffer an error later.
1577 AcceptInvalidationMessages();
1579 /* Look up the appropriate relation using namespace search. */
1580 state
.expected_relkind
= relkind
;
1581 state
.heap_lockmode
= drop
->concurrent
?
1582 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1583 /* We must initialize these fields to show that no locks are held: */
1584 state
.heapOid
= InvalidOid
;
1585 state
.partParentOid
= InvalidOid
;
1587 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1588 RangeVarCallbackForDropRelation
,
1592 if (!OidIsValid(relOid
))
1594 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1599 * Decide if concurrent mode needs to be used here or not. The
1600 * callback retrieved the rel's persistence for us.
1602 if (drop
->concurrent
&&
1603 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1605 Assert(list_length(drop
->objects
) == 1 &&
1606 drop
->removeType
== OBJECT_INDEX
);
1607 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1611 * Concurrent index drop cannot be used with partitioned indexes,
1614 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1615 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1617 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1618 errmsg("cannot drop partitioned index \"%s\" concurrently",
1622 * If we're told to drop a partitioned index, we must acquire lock on
1623 * all the children of its parent partitioned table before proceeding.
1624 * Otherwise we'd try to lock the child index partitions before their
1625 * tables, leading to potential deadlock against other sessions that
1626 * will lock those objects in the other order.
1628 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1629 (void) find_all_inheritors(state
.heapOid
,
1630 state
.heap_lockmode
,
1633 /* OK, we're ready to delete this one */
1634 obj
.classId
= RelationRelationId
;
1635 obj
.objectId
= relOid
;
1636 obj
.objectSubId
= 0;
1638 add_exact_object_address(&obj
, objects
);
1641 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1643 free_object_addresses(objects
);
1647 * Before acquiring a table lock, check whether we have sufficient rights.
1648 * In the case of DROP INDEX, also try to lock the table before the index.
1649 * Also, if the table to be dropped is a partition, we try to lock the parent
1653 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1657 struct DropRelationCallbackState
*state
;
1658 char expected_relkind
;
1660 Form_pg_class classform
;
1661 LOCKMODE heap_lockmode
;
1662 bool invalid_system_index
= false;
1664 state
= (struct DropRelationCallbackState
*) arg
;
1665 heap_lockmode
= state
->heap_lockmode
;
1668 * If we previously locked some other index's heap, and the name we're
1669 * looking up no longer refers to that relation, release the now-useless
1672 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1674 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1675 state
->heapOid
= InvalidOid
;
1679 * Similarly, if we previously locked some other partition's heap, and the
1680 * name we're looking up no longer refers to that relation, release the
1683 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1685 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1686 state
->partParentOid
= InvalidOid
;
1689 /* Didn't find a relation, so no need for locking or permission checks. */
1690 if (!OidIsValid(relOid
))
1693 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1694 if (!HeapTupleIsValid(tuple
))
1695 return; /* concurrently dropped, so nothing to do */
1696 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1697 is_partition
= classform
->relispartition
;
1699 /* Pass back some data to save lookups in RemoveRelations */
1700 state
->actual_relkind
= classform
->relkind
;
1701 state
->actual_relpersistence
= classform
->relpersistence
;
1704 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1705 * but RemoveRelations() can only pass one relkind for a given relation.
1706 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1707 * That means we must be careful before giving the wrong type error when
1708 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1709 * exists with indexes.
1711 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1712 expected_relkind
= RELKIND_RELATION
;
1713 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1714 expected_relkind
= RELKIND_INDEX
;
1716 expected_relkind
= classform
->relkind
;
1718 if (state
->expected_relkind
!= expected_relkind
)
1719 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1720 state
->expected_relkind
);
1722 /* Allow DROP to either table owner or schema owner */
1723 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1724 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1725 aclcheck_error(ACLCHECK_NOT_OWNER
,
1726 get_relkind_objtype(classform
->relkind
),
1730 * Check the case of a system index that might have been invalidated by a
1731 * failed concurrent process and allow its drop. For the time being, this
1732 * only concerns indexes of toast relations that became invalid during a
1733 * REINDEX CONCURRENTLY process.
1735 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1738 Form_pg_index indexform
;
1741 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1742 if (!HeapTupleIsValid(locTuple
))
1744 ReleaseSysCache(tuple
);
1748 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1749 indisvalid
= indexform
->indisvalid
;
1750 ReleaseSysCache(locTuple
);
1752 /* Mark object as being an invalid index of system catalogs */
1754 invalid_system_index
= true;
1757 /* In the case of an invalid index, it is fine to bypass this check */
1758 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1760 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1761 errmsg("permission denied: \"%s\" is a system catalog",
1764 ReleaseSysCache(tuple
);
1767 * In DROP INDEX, attempt to acquire lock on the parent table before
1768 * locking the index. index_drop() will need this anyway, and since
1769 * regular queries lock tables before their indexes, we risk deadlock if
1770 * we do it the other way around. No error if we don't find a pg_index
1771 * entry, though --- the relation may have been dropped. Note that this
1772 * code will execute for either plain or partitioned indexes.
1774 if (expected_relkind
== RELKIND_INDEX
&&
1775 relOid
!= oldRelOid
)
1777 state
->heapOid
= IndexGetRelation(relOid
, true);
1778 if (OidIsValid(state
->heapOid
))
1779 LockRelationOid(state
->heapOid
, heap_lockmode
);
1783 * Similarly, if the relation is a partition, we must acquire lock on its
1784 * parent before locking the partition. That's because queries lock the
1785 * parent before its partitions, so we risk deadlock if we do it the other
1788 if (is_partition
&& relOid
!= oldRelOid
)
1790 state
->partParentOid
= get_partition_parent(relOid
, true);
1791 if (OidIsValid(state
->partParentOid
))
1792 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1798 * Executes a TRUNCATE command.
1800 * This is a multi-relation truncate. We first open and grab exclusive
1801 * lock on all relations involved, checking permissions and otherwise
1802 * verifying that the relation is OK for truncation. Note that if relations
1803 * are foreign tables, at this stage, we have not yet checked that their
1804 * foreign data in external data sources are OK for truncation. These are
1805 * checked when foreign data are actually truncated later. In CASCADE mode,
1806 * relations having FK references to the targeted relations are automatically
1807 * added to the group; in RESTRICT mode, we check that all FK references are
1808 * internal to the group that's being truncated. Finally all the relations
1809 * are truncated and reindexed.
1812 ExecuteTruncate(TruncateStmt
*stmt
)
1816 List
*relids_logged
= NIL
;
1820 * Open, exclusive-lock, and check all the explicitly-specified relations
1822 foreach(cell
, stmt
->relations
)
1824 RangeVar
*rv
= lfirst(cell
);
1826 bool recurse
= rv
->inh
;
1828 LOCKMODE lockmode
= AccessExclusiveLock
;
1830 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1831 0, RangeVarCallbackForTruncate
,
1834 /* don't throw error for "TRUNCATE foo, foo" */
1835 if (list_member_oid(relids
, myrelid
))
1838 /* open the relation, we already hold a lock on it */
1839 rel
= table_open(myrelid
, NoLock
);
1842 * RangeVarGetRelidExtended() has done most checks with its callback,
1843 * but other checks with the now-opened Relation remain.
1845 truncate_check_activity(rel
);
1847 rels
= lappend(rels
, rel
);
1848 relids
= lappend_oid(relids
, myrelid
);
1850 /* Log this relation only if needed for logical decoding */
1851 if (RelationIsLogicallyLogged(rel
))
1852 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1859 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1861 foreach(child
, children
)
1863 Oid childrelid
= lfirst_oid(child
);
1865 if (list_member_oid(relids
, childrelid
))
1868 /* find_all_inheritors already got lock */
1869 rel
= table_open(childrelid
, NoLock
);
1872 * It is possible that the parent table has children that are
1873 * temp tables of other backends. We cannot safely access
1874 * such tables (because of buffering issues), and the best
1875 * thing to do is to silently ignore them. Note that this
1876 * check is the same as one of the checks done in
1877 * truncate_check_activity() called below, still it is kept
1878 * here for simplicity.
1880 if (RELATION_IS_OTHER_TEMP(rel
))
1882 table_close(rel
, lockmode
);
1887 * Inherited TRUNCATE commands perform access permission
1888 * checks on the parent table only. So we skip checking the
1889 * children's permissions and don't call
1890 * truncate_check_perms() here.
1892 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1893 truncate_check_activity(rel
);
1895 rels
= lappend(rels
, rel
);
1896 relids
= lappend_oid(relids
, childrelid
);
1898 /* Log this relation only if needed for logical decoding */
1899 if (RelationIsLogicallyLogged(rel
))
1900 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1903 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1905 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1906 errmsg("cannot truncate only a partitioned table"),
1907 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1910 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1911 stmt
->behavior
, stmt
->restart_seqs
, false);
1913 /* And close the rels */
1916 Relation rel
= (Relation
) lfirst(cell
);
1918 table_close(rel
, NoLock
);
1923 * ExecuteTruncateGuts
1925 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1926 * command (see above) as well as replication subscribers that execute a
1927 * replicated TRUNCATE action.
1929 * explicit_rels is the list of Relations to truncate that the command
1930 * specified. relids is the list of Oids corresponding to explicit_rels.
1931 * relids_logged is the list of Oids (a subset of relids) that require
1932 * WAL-logging. This is all a bit redundant, but the existing callers have
1933 * this information handy in this form.
1936 ExecuteTruncateGuts(List
*explicit_rels
,
1938 List
*relids_logged
,
1939 DropBehavior behavior
, bool restart_seqs
,
1940 bool run_as_table_owner
)
1943 List
*seq_relids
= NIL
;
1944 HTAB
*ft_htab
= NULL
;
1946 ResultRelInfo
*resultRelInfos
;
1947 ResultRelInfo
*resultRelInfo
;
1948 SubTransactionId mySubid
;
1953 * Check the explicitly-specified relations.
1955 * In CASCADE mode, suck in all referencing relations as well. This
1956 * requires multiple iterations to find indirectly-dependent relations. At
1957 * each phase, we need to exclusive-lock new rels before looking for their
1958 * dependencies, else we might miss something. Also, we check each rel as
1959 * soon as we open it, to avoid a faux pas such as holding lock for a long
1960 * time on a rel we have no permissions for.
1962 rels
= list_copy(explicit_rels
);
1963 if (behavior
== DROP_CASCADE
)
1969 newrelids
= heap_truncate_find_FKs(relids
);
1970 if (newrelids
== NIL
)
1971 break; /* nothing else to add */
1973 foreach(cell
, newrelids
)
1975 Oid relid
= lfirst_oid(cell
);
1978 rel
= table_open(relid
, AccessExclusiveLock
);
1980 (errmsg("truncate cascades to table \"%s\"",
1981 RelationGetRelationName(rel
))));
1982 truncate_check_rel(relid
, rel
->rd_rel
);
1983 truncate_check_perms(relid
, rel
->rd_rel
);
1984 truncate_check_activity(rel
);
1985 rels
= lappend(rels
, rel
);
1986 relids
= lappend_oid(relids
, relid
);
1988 /* Log this relation only if needed for logical decoding */
1989 if (RelationIsLogicallyLogged(rel
))
1990 relids_logged
= lappend_oid(relids_logged
, relid
);
1996 * Check foreign key references. In CASCADE mode, this should be
1997 * unnecessary since we just pulled in all the references; but as a
1998 * cross-check, do it anyway if in an Assert-enabled build.
2000 #ifdef USE_ASSERT_CHECKING
2001 heap_truncate_check_FKs(rels
, false);
2003 if (behavior
== DROP_RESTRICT
)
2004 heap_truncate_check_FKs(rels
, false);
2008 * If we are asked to restart sequences, find all the sequences, lock them
2009 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2010 * We want to do this early since it's pointless to do all the truncation
2011 * work only to fail on sequence permissions.
2017 Relation rel
= (Relation
) lfirst(cell
);
2018 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
2021 foreach(seqcell
, seqlist
)
2023 Oid seq_relid
= lfirst_oid(seqcell
);
2026 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
2028 /* This check must match AlterSequence! */
2029 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
2030 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
2031 RelationGetRelationName(seq_rel
));
2033 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
2035 relation_close(seq_rel
, NoLock
);
2040 /* Prepare to catch AFTER triggers. */
2041 AfterTriggerBeginQuery();
2044 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2045 * each relation. We don't need to call ExecOpenIndices, though.
2047 * We put the ResultRelInfos in the es_opened_result_relations list, even
2048 * though we don't have a range table and don't populate the
2049 * es_result_relations array. That's a bit bogus, but it's enough to make
2050 * ExecGetTriggerResultRel() find them.
2052 estate
= CreateExecutorState();
2053 resultRelInfos
= (ResultRelInfo
*)
2054 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
2055 resultRelInfo
= resultRelInfos
;
2058 Relation rel
= (Relation
) lfirst(cell
);
2060 InitResultRelInfo(resultRelInfo
,
2062 0, /* dummy rangetable index */
2065 estate
->es_opened_result_relations
=
2066 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
2071 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2072 * truncating (this is because one of them might throw an error). Also, if
2073 * we were to allow them to prevent statement execution, that would need
2074 * to be handled here.
2076 resultRelInfo
= resultRelInfos
;
2081 if (run_as_table_owner
)
2082 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2084 ExecBSTruncateTriggers(estate
, resultRelInfo
);
2085 if (run_as_table_owner
)
2086 RestoreUserContext(&ucxt
);
2091 * OK, truncate each table.
2093 mySubid
= GetCurrentSubTransactionId();
2097 Relation rel
= (Relation
) lfirst(cell
);
2099 /* Skip partitioned tables as there is nothing to do */
2100 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
2104 * Build the lists of foreign tables belonging to each foreign server
2105 * and pass each list to the foreign data wrapper's callback function,
2106 * so that each server can truncate its all foreign tables in bulk.
2107 * Each list is saved as a single entry in a hash table that uses the
2108 * server OID as lookup key.
2110 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
2112 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
2114 ForeignTruncateInfo
*ft_info
;
2116 /* First time through, initialize hashtable for foreign tables */
2121 memset(&hctl
, 0, sizeof(HASHCTL
));
2122 hctl
.keysize
= sizeof(Oid
);
2123 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
2124 hctl
.hcxt
= CurrentMemoryContext
;
2126 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
2127 32, /* start small and extend */
2129 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
2132 /* Find or create cached entry for the foreign table */
2133 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
2135 ft_info
->rels
= NIL
;
2138 * Save the foreign table in the entry of the server that the
2139 * foreign table belongs to.
2141 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2146 * Normally, we need a transaction-safe truncation here. However, if
2147 * the table was either created in the current (sub)transaction or has
2148 * a new relfilenumber in the current (sub)transaction, then we can
2149 * just truncate it in-place, because a rollback would cause the whole
2150 * table or the current physical file to be thrown away anyway.
2152 if (rel
->rd_createSubid
== mySubid
||
2153 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2155 /* Immediate, non-rollbackable truncation is OK */
2156 heap_truncate_one_rel(rel
);
2162 ReindexParams reindex_params
= {0};
2165 * This effectively deletes all rows in the table, and may be done
2166 * in a serializable transaction. In that case we must record a
2167 * rw-conflict in to this transaction from each transaction
2168 * holding a predicate lock on the table.
2170 CheckTableForSerializableConflictIn(rel
);
2173 * Need the full transaction-safe pushups.
2175 * Create a new empty storage file for the relation, and assign it
2176 * as the relfilenumber value. The old storage file is scheduled
2177 * for deletion at commit.
2179 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2181 heap_relid
= RelationGetRelid(rel
);
2184 * The same for the toast table, if any.
2186 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2187 if (OidIsValid(toast_relid
))
2189 Relation toastrel
= relation_open(toast_relid
,
2190 AccessExclusiveLock
);
2192 RelationSetNewRelfilenumber(toastrel
,
2193 toastrel
->rd_rel
->relpersistence
);
2194 table_close(toastrel
, NoLock
);
2198 * Reconstruct the indexes to match, and we're done.
2200 reindex_relation(NULL
, heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2204 pgstat_count_truncate(rel
);
2207 /* Now go through the hash table, and truncate foreign tables */
2210 ForeignTruncateInfo
*ft_info
;
2211 HASH_SEQ_STATUS seq
;
2213 hash_seq_init(&seq
, ft_htab
);
2217 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2219 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2221 /* truncate_check_rel() has checked that already */
2222 Assert(routine
->ExecForeignTruncate
!= NULL
);
2224 routine
->ExecForeignTruncate(ft_info
->rels
,
2231 hash_destroy(ft_htab
);
2237 * Restart owned sequences if we were asked to.
2239 foreach(cell
, seq_relids
)
2241 Oid seq_relid
= lfirst_oid(cell
);
2243 ResetSequence(seq_relid
);
2247 * Write a WAL record to allow this set of actions to be logically
2250 * Assemble an array of relids so we can write a single WAL record for the
2253 if (relids_logged
!= NIL
)
2255 xl_heap_truncate xlrec
;
2258 /* should only get here if wal_level >= logical */
2259 Assert(XLogLogicalInfoActive());
2261 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2262 foreach(cell
, relids_logged
)
2263 logrelids
[i
++] = lfirst_oid(cell
);
2265 xlrec
.dbId
= MyDatabaseId
;
2266 xlrec
.nrelids
= list_length(relids_logged
);
2268 if (behavior
== DROP_CASCADE
)
2269 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2271 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2274 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2275 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2277 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2279 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2283 * Process all AFTER STATEMENT TRUNCATE triggers.
2285 resultRelInfo
= resultRelInfos
;
2290 if (run_as_table_owner
)
2291 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2293 ExecASTruncateTriggers(estate
, resultRelInfo
);
2294 if (run_as_table_owner
)
2295 RestoreUserContext(&ucxt
);
2299 /* Handle queued AFTER triggers */
2300 AfterTriggerEndQuery(estate
);
2302 /* We can clean up the EState now */
2303 FreeExecutorState(estate
);
2306 * Close any rels opened by CASCADE (can't do this while EState still
2309 rels
= list_difference_ptr(rels
, explicit_rels
);
2312 Relation rel
= (Relation
) lfirst(cell
);
2314 table_close(rel
, NoLock
);
2319 * Check that a given relation is safe to truncate. Subroutine for
2320 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2323 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2325 char *relname
= NameStr(reltuple
->relname
);
2328 * Only allow truncate on regular tables, foreign tables using foreign
2329 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2330 * latter are only being included here for the following checks; no
2331 * physical truncation will occur in their case.).
2333 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2335 Oid serverid
= GetForeignServerIdByRelId(relid
);
2336 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2338 if (!fdwroutine
->ExecForeignTruncate
)
2340 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2341 errmsg("cannot truncate foreign table \"%s\"",
2344 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2345 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2347 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2348 errmsg("\"%s\" is not a table", relname
)));
2351 * Most system catalogs can't be truncated at all, or at least not unless
2352 * allow_system_table_mods=on. As an exception, however, we allow
2353 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2354 * to change its relfilenode to match the old cluster, and allowing a
2355 * TRUNCATE command to be executed is the easiest way of doing that.
2357 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2358 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2360 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2361 errmsg("permission denied: \"%s\" is a system catalog",
2364 InvokeObjectTruncateHook(relid
);
2368 * Check that current user has the permission to truncate given relation.
2371 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2373 char *relname
= NameStr(reltuple
->relname
);
2374 AclResult aclresult
;
2376 /* Permissions checks */
2377 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2378 if (aclresult
!= ACLCHECK_OK
)
2379 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2384 * Set of extra sanity checks to check if a given relation is safe to
2385 * truncate. This is split with truncate_check_rel() as
2386 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2389 truncate_check_activity(Relation rel
)
2392 * Don't allow truncate on temp tables of other backends ... their local
2393 * buffer manager is not going to cope.
2395 if (RELATION_IS_OTHER_TEMP(rel
))
2397 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2398 errmsg("cannot truncate temporary tables of other sessions")));
2401 * Also check for active uses of the relation in the current transaction,
2402 * including open scans and pending AFTER trigger events.
2404 CheckTableNotInUse(rel
, "TRUNCATE");
2409 * returns the name corresponding to a typstorage/attstorage enum value
2412 storage_name(char c
)
2416 case TYPSTORAGE_PLAIN
:
2418 case TYPSTORAGE_EXTERNAL
:
2420 case TYPSTORAGE_EXTENDED
:
2422 case TYPSTORAGE_MAIN
:
2431 * Returns new schema given initial schema and superclasses.
2434 * 'columns' is the column/attribute definition for the table. (It's a list
2435 * of ColumnDef's.) It is destructively changed.
2436 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2437 * 'relpersistence' is the persistence type of the table.
2438 * 'is_partition' tells if the table is a partition.
2441 * 'supconstr' receives a list of CookedConstraint representing
2442 * CHECK constraints belonging to parent relations, updated as
2443 * necessary to be valid for the child.
2444 * 'supnotnulls' receives a list of CookedConstraint representing
2445 * not-null constraints based on those from parent relations.
2448 * Completed schema list.
2451 * The order in which the attributes are inherited is very important.
2452 * Intuitively, the inherited attributes should come first. If a table
2453 * inherits from multiple parents, the order of those attributes are
2454 * according to the order of the parents specified in CREATE TABLE.
2456 * Here's an example:
2458 * create table person (name text, age int4, location point);
2459 * create table emp (salary int4, manager text) inherits(person);
2460 * create table student (gpa float8) inherits (person);
2461 * create table stud_emp (percent int4) inherits (emp, student);
2463 * The order of the attributes of stud_emp is:
2465 * person {1:name, 2:age, 3:location}
2467 * {6:gpa} student emp {4:salary, 5:manager}
2469 * stud_emp {7:percent}
2471 * If the same attribute name appears multiple times, then it appears
2472 * in the result table in the proper location for its first appearance.
2474 * Constraints (including not-null constraints) for the child table
2475 * are the union of all relevant constraints, from both the child schema
2476 * and parent tables. In addition, in legacy inheritance, each column that
2477 * appears in a primary key in any of the parents also gets a NOT NULL
2478 * constraint (partitioning doesn't need this, because the PK itself gets
2481 * The default value for a child column is defined as:
2482 * (1) If the child schema specifies a default, that value is used.
2483 * (2) If neither the child nor any parent specifies a default, then
2484 * the column will not have a default.
2485 * (3) If conflicting defaults are inherited from different parents
2486 * (and not overridden by the child), an error is raised.
2487 * (4) Otherwise the inherited default is used.
2489 * Note that the default-value infrastructure is used for generated
2490 * columns' expressions too, so most of the preceding paragraph applies
2491 * to generation expressions too. We insist that a child column be
2492 * generated if and only if its parent(s) are, but it need not have
2493 * the same generation expression.
2497 MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
2498 bool is_partition
, List
**supconstr
, List
**supnotnulls
)
2500 List
*inh_columns
= NIL
;
2501 List
*constraints
= NIL
;
2502 List
*nnconstraints
= NIL
;
2503 bool have_bogus_defaults
= false;
2505 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2506 List
*saved_columns
= NIL
;
2510 * Check for and reject tables with too many columns. We perform this
2511 * check relatively early for two reasons: (a) we don't run the risk of
2512 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2513 * okay if we're processing <= 1600 columns, but could take minutes to
2514 * execute if the user attempts to create a table with hundreds of
2515 * thousands of columns.
2517 * Note that we also need to check that we do not exceed this figure after
2518 * including columns from inherited relations.
2520 if (list_length(columns
) > MaxHeapAttributeNumber
)
2522 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2523 errmsg("tables can have at most %d columns",
2524 MaxHeapAttributeNumber
)));
2527 * Check for duplicate names in the explicit list of attributes.
2529 * Although we might consider merging such entries in the same way that we
2530 * handle name conflicts for inherited attributes, it seems to make more
2531 * sense to assume such conflicts are errors.
2533 * We don't use foreach() here because we have two nested loops over the
2534 * columns list, with possible element deletions in the inner one. If we
2535 * used foreach_delete_current() it could only fix up the state of one of
2536 * the loops, so it seems cleaner to use looping over list indexes for
2537 * both loops. Note that any deletion will happen beyond where the outer
2538 * loop is, so its index never needs adjustment.
2540 for (int coldefpos
= 0; coldefpos
< list_length(columns
); coldefpos
++)
2542 ColumnDef
*coldef
= list_nth_node(ColumnDef
, columns
, coldefpos
);
2544 if (!is_partition
&& coldef
->typeName
== NULL
)
2547 * Typed table column option that does not belong to a column from
2548 * the type. This works because the columns from the type come
2549 * first in the list. (We omit this check for partition column
2550 * lists; those are processed separately below.)
2553 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2554 errmsg("column \"%s\" does not exist",
2558 /* restpos scans all entries beyond coldef; incr is in loop body */
2559 for (int restpos
= coldefpos
+ 1; restpos
< list_length(columns
);)
2561 ColumnDef
*restdef
= list_nth_node(ColumnDef
, columns
, restpos
);
2563 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2565 if (coldef
->is_from_type
)
2568 * merge the column options into the column from the type
2570 coldef
->is_not_null
= restdef
->is_not_null
;
2571 coldef
->raw_default
= restdef
->raw_default
;
2572 coldef
->cooked_default
= restdef
->cooked_default
;
2573 coldef
->constraints
= restdef
->constraints
;
2574 coldef
->is_from_type
= false;
2575 columns
= list_delete_nth_cell(columns
, restpos
);
2579 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2580 errmsg("column \"%s\" specified more than once",
2589 * In case of a partition, there are no new column definitions, only dummy
2590 * ColumnDefs created for column constraints. Set them aside for now and
2591 * process them at the end.
2595 saved_columns
= columns
;
2600 * Scan the parents left-to-right, and merge their attributes to form a
2601 * list of inherited columns (inh_columns).
2606 Oid parent
= lfirst_oid(lc
);
2608 TupleDesc tupleDesc
;
2609 TupleConstr
*constr
;
2611 List
*inherited_defaults
;
2612 List
*cols_with_defaults
;
2616 Bitmapset
*nncols
= NULL
;
2618 /* caller already got lock */
2619 relation
= table_open(parent
, NoLock
);
2622 * Check for active uses of the parent partitioned table in the
2623 * current transaction, such as being used in some manner by an
2624 * enclosing command.
2627 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2630 * We do not allow partitioned tables and partitions to participate in
2631 * regular inheritance.
2633 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !is_partition
)
2635 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2636 errmsg("cannot inherit from partitioned table \"%s\"",
2637 RelationGetRelationName(relation
))));
2638 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2640 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2641 errmsg("cannot inherit from partition \"%s\"",
2642 RelationGetRelationName(relation
))));
2644 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2645 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2646 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2648 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2649 errmsg("inherited relation \"%s\" is not a table or foreign table",
2650 RelationGetRelationName(relation
))));
2653 * If the parent is permanent, so must be all of its partitions. Note
2654 * that inheritance allows that case.
2657 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2658 relpersistence
== RELPERSISTENCE_TEMP
)
2660 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2661 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2662 RelationGetRelationName(relation
))));
2664 /* Permanent rels cannot inherit from temporary ones */
2665 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2666 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2668 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2669 errmsg(!is_partition
2670 ? "cannot inherit from temporary relation \"%s\""
2671 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2672 RelationGetRelationName(relation
))));
2674 /* If existing rel is temp, it must belong to this session */
2675 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2676 !relation
->rd_islocaltemp
)
2678 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2679 errmsg(!is_partition
2680 ? "cannot inherit from temporary relation of another session"
2681 : "cannot create as partition of temporary relation of another session")));
2684 * We should have an UNDER permission flag for this, but for now,
2685 * demand that creator of a child table own the parent.
2687 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2688 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2689 RelationGetRelationName(relation
));
2691 tupleDesc
= RelationGetDescr(relation
);
2692 constr
= tupleDesc
->constr
;
2695 * newattmap->attnums[] will contain the child-table attribute numbers
2696 * for the attributes of this parent table. (They are not the same
2697 * for parents after the first one, nor if we have dropped columns.)
2699 newattmap
= make_attrmap(tupleDesc
->natts
);
2701 /* We can't process inherited defaults until newattmap is complete. */
2702 inherited_defaults
= cols_with_defaults
= NIL
;
2705 * Request attnotnull on columns that have a not-null constraint
2706 * that's not marked NO INHERIT.
2708 nnconstrs
= RelationGetNotNullConstraints(RelationGetRelid(relation
),
2710 foreach_ptr(CookedConstraint
, cc
, nnconstrs
)
2711 nncols
= bms_add_member(nncols
, cc
->attnum
);
2713 for (AttrNumber parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2716 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2718 char *attributeName
= NameStr(attribute
->attname
);
2721 ColumnDef
*mergeddef
;
2724 * Ignore dropped columns in the parent.
2726 if (attribute
->attisdropped
)
2727 continue; /* leave newattmap->attnums entry as zero */
2730 * Create new column definition
2732 newdef
= makeColumnDef(attributeName
, attribute
->atttypid
,
2733 attribute
->atttypmod
, attribute
->attcollation
);
2734 newdef
->storage
= attribute
->attstorage
;
2735 newdef
->generated
= attribute
->attgenerated
;
2736 if (CompressionMethodIsValid(attribute
->attcompression
))
2737 newdef
->compression
=
2738 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2741 * Regular inheritance children are independent enough not to
2742 * inherit identity columns. But partitions are integral part of
2743 * a partitioned table and inherit identity column.
2746 newdef
->identity
= attribute
->attidentity
;
2749 * Does it match some previously considered column from another
2752 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2753 if (exist_attno
> 0)
2756 * Yes, try to merge the two column definitions.
2758 mergeddef
= MergeInheritedAttribute(inh_columns
, exist_attno
, newdef
);
2760 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2763 * Partitions have only one parent, so conflict should never
2766 Assert(!is_partition
);
2771 * No, create a new inherited column
2773 newdef
->inhcount
= 1;
2774 newdef
->is_local
= false;
2775 inh_columns
= lappend(inh_columns
, newdef
);
2777 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2782 * mark attnotnull if parent has it
2784 if (bms_is_member(parent_attno
, nncols
))
2785 mergeddef
->is_not_null
= true;
2788 * Locate default/generation expression if any
2790 if (attribute
->atthasdef
)
2794 this_default
= TupleDescGetDefault(tupleDesc
, parent_attno
);
2795 if (this_default
== NULL
)
2796 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2797 parent_attno
, RelationGetRelationName(relation
));
2800 * If it's a GENERATED default, it might contain Vars that
2801 * need to be mapped to the inherited column(s)' new numbers.
2802 * We can't do that till newattmap is ready, so just remember
2803 * all the inherited default expressions for the moment.
2805 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2806 cols_with_defaults
= lappend(cols_with_defaults
, mergeddef
);
2811 * Now process any inherited default expressions, adjusting attnos
2812 * using the completed newattmap map.
2814 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2816 Node
*this_default
= (Node
*) lfirst(lc1
);
2817 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2818 bool found_whole_row
;
2820 /* Adjust Vars to match new table's column numbering */
2821 this_default
= map_variable_attnos(this_default
,
2824 InvalidOid
, &found_whole_row
);
2827 * For the moment we have to reject whole-row variables. We could
2828 * convert them, if we knew the new table's rowtype OID, but that
2829 * hasn't been assigned yet. (A variable could only appear in a
2830 * generation expression, so the error message is correct.)
2832 if (found_whole_row
)
2834 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2835 errmsg("cannot convert whole-row table reference"),
2836 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2838 RelationGetRelationName(relation
))));
2841 * If we already had a default from some prior parent, check to
2842 * see if they are the same. If so, no problem; if not, mark the
2843 * column as having a bogus default. Below, we will complain if
2844 * the bogus default isn't overridden by the child columns.
2846 Assert(def
->raw_default
== NULL
);
2847 if (def
->cooked_default
== NULL
)
2848 def
->cooked_default
= this_default
;
2849 else if (!equal(def
->cooked_default
, this_default
))
2851 def
->cooked_default
= &bogus_marker
;
2852 have_bogus_defaults
= true;
2857 * Now copy the CHECK constraints of this parent, adjusting attnos
2858 * using the completed newattmap map. Identically named constraints
2859 * are merged if possible, else we throw error.
2861 if (constr
&& constr
->num_check
> 0)
2863 ConstrCheck
*check
= constr
->check
;
2865 for (int i
= 0; i
< constr
->num_check
; i
++)
2867 char *name
= check
[i
].ccname
;
2869 bool found_whole_row
;
2871 /* ignore if the constraint is non-inheritable */
2872 if (check
[i
].ccnoinherit
)
2875 /* Adjust Vars to match new table's column numbering */
2876 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2879 InvalidOid
, &found_whole_row
);
2882 * For the moment we have to reject whole-row variables. We
2883 * could convert them, if we knew the new table's rowtype OID,
2884 * but that hasn't been assigned yet.
2886 if (found_whole_row
)
2888 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2889 errmsg("cannot convert whole-row table reference"),
2890 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2892 RelationGetRelationName(relation
))));
2894 constraints
= MergeCheckConstraint(constraints
, name
, expr
,
2895 check
[i
].ccenforced
);
2900 * Also copy the not-null constraints from this parent. The
2901 * attnotnull markings were already installed above.
2903 foreach_ptr(CookedConstraint
, nn
, nnconstrs
)
2905 Assert(nn
->contype
== CONSTR_NOTNULL
);
2907 nn
->attnum
= newattmap
->attnums
[nn
->attnum
- 1];
2909 nnconstraints
= lappend(nnconstraints
, nn
);
2912 free_attrmap(newattmap
);
2915 * Close the parent rel, but keep our lock on it until xact commit.
2916 * That will prevent someone else from deleting or ALTERing the parent
2917 * before the child is committed.
2919 table_close(relation
, NoLock
);
2923 * If we had no inherited attributes, the result columns are just the
2924 * explicitly declared columns. Otherwise, we need to merge the declared
2925 * columns into the inherited column list. Although, we never have any
2926 * explicitly declared columns if the table is a partition.
2928 if (inh_columns
!= NIL
)
2930 int newcol_attno
= 0;
2932 foreach(lc
, columns
)
2934 ColumnDef
*newdef
= lfirst_node(ColumnDef
, lc
);
2935 char *attributeName
= newdef
->colname
;
2939 * Partitions have only one parent and have no column definitions
2940 * of their own, so conflict should never occur.
2942 Assert(!is_partition
);
2947 * Does it match some inherited column?
2949 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2950 if (exist_attno
> 0)
2953 * Yes, try to merge the two column definitions.
2955 MergeChildAttribute(inh_columns
, exist_attno
, newcol_attno
, newdef
);
2960 * No, attach new column unchanged to result columns.
2962 inh_columns
= lappend(inh_columns
, newdef
);
2966 columns
= inh_columns
;
2969 * Check that we haven't exceeded the legal # of columns after merging
2970 * in inherited columns.
2972 if (list_length(columns
) > MaxHeapAttributeNumber
)
2974 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2975 errmsg("tables can have at most %d columns",
2976 MaxHeapAttributeNumber
)));
2980 * Now that we have the column definition list for a partition, we can
2981 * check whether the columns referenced in the column constraint specs
2982 * actually exist. Also, merge column defaults.
2986 foreach(lc
, saved_columns
)
2988 ColumnDef
*restdef
= lfirst(lc
);
2994 ColumnDef
*coldef
= lfirst(l
);
2996 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
3001 * Check for conflicts related to generated columns.
3003 * Same rules as above: generated-ness has to match the
3004 * parent, but the contents of the generation expression
3007 if (coldef
->generated
)
3009 if (restdef
->raw_default
&& !restdef
->generated
)
3011 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3012 errmsg("column \"%s\" inherits from generated column but specifies default",
3013 restdef
->colname
)));
3014 if (restdef
->identity
)
3016 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3017 errmsg("column \"%s\" inherits from generated column but specifies identity",
3018 restdef
->colname
)));
3022 if (restdef
->generated
)
3024 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3025 errmsg("child column \"%s\" specifies generation expression",
3027 errhint("A child table column cannot be generated unless its parent column is.")));
3031 * Override the parent's default value for this column
3032 * (coldef->cooked_default) with the partition's local
3033 * definition (restdef->raw_default), if there's one. It
3034 * should be physically impossible to get a cooked default
3035 * in the local definition or a raw default in the
3036 * inherited definition, but make sure they're nulls, for
3039 Assert(restdef
->cooked_default
== NULL
);
3040 Assert(coldef
->raw_default
== NULL
);
3041 if (restdef
->raw_default
)
3043 coldef
->raw_default
= restdef
->raw_default
;
3044 coldef
->cooked_default
= NULL
;
3049 /* complain for constraints on columns not in parent */
3052 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3053 errmsg("column \"%s\" does not exist",
3054 restdef
->colname
)));
3059 * If we found any conflicting parent default values, check to make sure
3060 * they were overridden by the child.
3062 if (have_bogus_defaults
)
3064 foreach(lc
, columns
)
3066 ColumnDef
*def
= lfirst(lc
);
3068 if (def
->cooked_default
== &bogus_marker
)
3072 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3073 errmsg("column \"%s\" inherits conflicting generation expressions",
3075 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3078 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3079 errmsg("column \"%s\" inherits conflicting default values",
3081 errhint("To resolve the conflict, specify a default explicitly.")));
3086 *supconstr
= constraints
;
3087 *supnotnulls
= nnconstraints
;
3094 * MergeCheckConstraint
3095 * Try to merge an inherited CHECK constraint with previous ones
3097 * If we inherit identically-named constraints from multiple parents, we must
3098 * merge them, or throw an error if they don't have identical definitions.
3100 * constraints is a list of CookedConstraint structs for previous constraints.
3102 * If the new constraint matches an existing one, then the existing
3103 * constraint's inheritance count is updated. If there is a conflict (same
3104 * name but different expression), throw an error. If the constraint neither
3105 * matches nor conflicts with an existing one, a new constraint is appended to
3109 MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
, bool is_enforced
)
3112 CookedConstraint
*newcon
;
3114 foreach(lc
, constraints
)
3116 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3118 Assert(ccon
->contype
== CONSTR_CHECK
);
3120 /* Non-matching names never conflict */
3121 if (strcmp(ccon
->name
, name
) != 0)
3124 if (equal(expr
, ccon
->expr
))
3126 /* OK to merge constraint with existing */
3127 if (pg_add_s16_overflow(ccon
->inhcount
, 1,
3130 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3131 errmsg("too many inheritance parents"));
3134 * When enforceability differs, the merged constraint should be
3135 * marked as ENFORCED because one of the parents is ENFORCED.
3137 if (!ccon
->is_enforced
&& is_enforced
)
3139 ccon
->is_enforced
= true;
3140 ccon
->skip_validation
= false;
3147 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3148 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3153 * Constraint couldn't be merged with an existing one and also didn't
3154 * conflict with an existing one, so add it as a new one to the list.
3156 newcon
= palloc0_object(CookedConstraint
);
3157 newcon
->contype
= CONSTR_CHECK
;
3158 newcon
->name
= pstrdup(name
);
3159 newcon
->expr
= expr
;
3160 newcon
->inhcount
= 1;
3161 newcon
->is_enforced
= is_enforced
;
3162 newcon
->skip_validation
= !is_enforced
;
3163 return lappend(constraints
, newcon
);
3167 * MergeChildAttribute
3168 * Merge given child attribute definition into given inherited attribute.
3171 * 'inh_columns' is the list of inherited ColumnDefs.
3172 * 'exist_attno' is the number of the inherited attribute in inh_columns
3173 * 'newcol_attno' is the attribute number in child table's schema definition
3174 * 'newdef' is the column/attribute definition from the child table.
3176 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3177 * ColumnDef remains unchanged.
3180 * - The attribute is merged according to the rules laid out in the prologue
3181 * of MergeAttributes().
3182 * - If matching inherited attribute exists but the child attribute can not be
3183 * merged into it, the function throws respective errors.
3184 * - A partition can not have its own column definitions. Hence this function
3185 * is applicable only to a regular inheritance child.
3188 MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
)
3190 char *attributeName
= newdef
->colname
;
3199 if (exist_attno
== newcol_attno
)
3201 (errmsg("merging column \"%s\" with inherited definition",
3205 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
3206 errdetail("User-specified column moved to the position of the inherited column.")));
3208 inhdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3211 * Must have the same type and typmod
3213 typenameTypeIdAndMod(NULL
, inhdef
->typeName
, &inhtypeid
, &inhtypmod
);
3214 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3215 if (inhtypeid
!= newtypeid
|| inhtypmod
!= newtypmod
)
3217 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3218 errmsg("column \"%s\" has a type conflict",
3220 errdetail("%s versus %s",
3221 format_type_with_typemod(inhtypeid
, inhtypmod
),
3222 format_type_with_typemod(newtypeid
, newtypmod
))));
3225 * Must have the same collation
3227 inhcollid
= GetColumnDefCollation(NULL
, inhdef
, inhtypeid
);
3228 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3229 if (inhcollid
!= newcollid
)
3231 (errcode(ERRCODE_COLLATION_MISMATCH
),
3232 errmsg("column \"%s\" has a collation conflict",
3234 errdetail("\"%s\" versus \"%s\"",
3235 get_collation_name(inhcollid
),
3236 get_collation_name(newcollid
))));
3239 * Identity is never inherited by a regular inheritance child. Pick
3240 * child's identity definition if there's one.
3242 inhdef
->identity
= newdef
->identity
;
3245 * Copy storage parameter
3247 if (inhdef
->storage
== 0)
3248 inhdef
->storage
= newdef
->storage
;
3249 else if (newdef
->storage
!= 0 && inhdef
->storage
!= newdef
->storage
)
3251 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3252 errmsg("column \"%s\" has a storage parameter conflict",
3254 errdetail("%s versus %s",
3255 storage_name(inhdef
->storage
),
3256 storage_name(newdef
->storage
))));
3259 * Copy compression parameter
3261 if (inhdef
->compression
== NULL
)
3262 inhdef
->compression
= newdef
->compression
;
3263 else if (newdef
->compression
!= NULL
)
3265 if (strcmp(inhdef
->compression
, newdef
->compression
) != 0)
3267 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3268 errmsg("column \"%s\" has a compression method conflict",
3270 errdetail("%s versus %s", inhdef
->compression
, newdef
->compression
)));
3274 * Merge of not-null constraints = OR 'em together
3276 inhdef
->is_not_null
|= newdef
->is_not_null
;
3279 * Check for conflicts related to generated columns.
3281 * If the parent column is generated, the child column will be made a
3282 * generated column if it isn't already. If it is a generated column,
3283 * we'll take its generation expression in preference to the parent's. We
3284 * must check that the child column doesn't specify a default value or
3285 * identity, which matches the rules for a single column in
3288 * Conversely, if the parent column is not generated, the child column
3289 * can't be either. (We used to allow that, but it results in being able
3290 * to override the generation expression via UPDATEs through the parent.)
3292 if (inhdef
->generated
)
3294 if (newdef
->raw_default
&& !newdef
->generated
)
3296 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3297 errmsg("column \"%s\" inherits from generated column but specifies default",
3299 if (newdef
->identity
)
3301 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3302 errmsg("column \"%s\" inherits from generated column but specifies identity",
3307 if (newdef
->generated
)
3309 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3310 errmsg("child column \"%s\" specifies generation expression",
3312 errhint("A child table column cannot be generated unless its parent column is.")));
3316 * If new def has a default, override previous default
3318 if (newdef
->raw_default
!= NULL
)
3320 inhdef
->raw_default
= newdef
->raw_default
;
3321 inhdef
->cooked_default
= newdef
->cooked_default
;
3324 /* Mark the column as locally defined */
3325 inhdef
->is_local
= true;
3329 * MergeInheritedAttribute
3330 * Merge given parent attribute definition into specified attribute
3331 * inherited from the previous parents.
3334 * 'inh_columns' is the list of previously inherited ColumnDefs.
3335 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3336 * 'newdef' is the new parent column/attribute definition to be merged.
3338 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3341 * - The attribute is merged according to the rules laid out in the prologue
3342 * of MergeAttributes().
3343 * - If matching inherited attribute exists but the new attribute can not be
3344 * merged into it, the function throws respective errors.
3345 * - A partition inherits from only a single parent. Hence this function is
3346 * applicable only to a regular inheritance.
3349 MergeInheritedAttribute(List
*inh_columns
,
3351 const ColumnDef
*newdef
)
3353 char *attributeName
= newdef
->colname
;
3363 (errmsg("merging multiple inherited definitions of column \"%s\"",
3365 prevdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3368 * Must have the same type and typmod
3370 typenameTypeIdAndMod(NULL
, prevdef
->typeName
, &prevtypeid
, &prevtypmod
);
3371 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3372 if (prevtypeid
!= newtypeid
|| prevtypmod
!= newtypmod
)
3374 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3375 errmsg("inherited column \"%s\" has a type conflict",
3377 errdetail("%s versus %s",
3378 format_type_with_typemod(prevtypeid
, prevtypmod
),
3379 format_type_with_typemod(newtypeid
, newtypmod
))));
3382 * Must have the same collation
3384 prevcollid
= GetColumnDefCollation(NULL
, prevdef
, prevtypeid
);
3385 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3386 if (prevcollid
!= newcollid
)
3388 (errcode(ERRCODE_COLLATION_MISMATCH
),
3389 errmsg("inherited column \"%s\" has a collation conflict",
3391 errdetail("\"%s\" versus \"%s\"",
3392 get_collation_name(prevcollid
),
3393 get_collation_name(newcollid
))));
3396 * Copy/check storage parameter
3398 if (prevdef
->storage
== 0)
3399 prevdef
->storage
= newdef
->storage
;
3400 else if (prevdef
->storage
!= newdef
->storage
)
3402 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3403 errmsg("inherited column \"%s\" has a storage parameter conflict",
3405 errdetail("%s versus %s",
3406 storage_name(prevdef
->storage
),
3407 storage_name(newdef
->storage
))));
3410 * Copy/check compression parameter
3412 if (prevdef
->compression
== NULL
)
3413 prevdef
->compression
= newdef
->compression
;
3414 else if (newdef
->compression
!= NULL
)
3416 if (strcmp(prevdef
->compression
, newdef
->compression
) != 0)
3418 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3419 errmsg("column \"%s\" has a compression method conflict",
3421 errdetail("%s versus %s",
3422 prevdef
->compression
, newdef
->compression
)));
3426 * Check for GENERATED conflicts
3428 if (prevdef
->generated
!= newdef
->generated
)
3430 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3431 errmsg("inherited column \"%s\" has a generation conflict",
3435 * Default and other constraints are handled by the caller.
3438 if (pg_add_s16_overflow(prevdef
->inhcount
, 1,
3439 &prevdef
->inhcount
))
3441 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3442 errmsg("too many inheritance parents"));
3448 * StoreCatalogInheritance
3449 * Updates the system catalogs with proper inheritance information.
3451 * supers is a list of the OIDs of the new relation's direct ancestors.
3454 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3455 bool child_is_partition
)
3464 Assert(OidIsValid(relationId
));
3470 * Store INHERITS information in pg_inherits using direct ancestors only.
3471 * Also enter dependencies on the direct ancestors, and make sure they are
3472 * marked with relhassubclass = true.
3474 * (Once upon a time, both direct and indirect ancestors were found here
3475 * and then entered into pg_ipl. Since that catalog doesn't exist
3476 * anymore, there's no need to look for indirect ancestors.)
3478 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3481 foreach(entry
, supers
)
3483 Oid parentOid
= lfirst_oid(entry
);
3485 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3486 child_is_partition
);
3490 table_close(relation
, RowExclusiveLock
);
3494 * Make catalog entries showing relationId as being an inheritance child
3495 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3498 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3499 int32 seqNumber
, Relation inhRelation
,
3500 bool child_is_partition
)
3502 ObjectAddress childobject
,
3505 /* store the pg_inherits row */
3506 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3509 * Store a dependency too
3511 parentobject
.classId
= RelationRelationId
;
3512 parentobject
.objectId
= parentOid
;
3513 parentobject
.objectSubId
= 0;
3514 childobject
.classId
= RelationRelationId
;
3515 childobject
.objectId
= relationId
;
3516 childobject
.objectSubId
= 0;
3518 recordDependencyOn(&childobject
, &parentobject
,
3519 child_dependency_type(child_is_partition
));
3522 * Post creation hook of this inheritance. Since object_access_hook
3523 * doesn't take multiple object identifiers, we relay oid of parent
3524 * relation using auxiliary_id argument.
3526 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3531 * Mark the parent as having subclasses.
3533 SetRelationHasSubclass(parentOid
, true);
3537 * Look for an existing column entry with the given name.
3539 * Returns the index (starting with 1) if attribute already exists in columns,
3543 findAttrByName(const char *attributeName
, const List
*columns
)
3548 foreach(lc
, columns
)
3550 if (strcmp(attributeName
, lfirst_node(ColumnDef
, lc
)->colname
) == 0)
3560 * SetRelationHasSubclass
3561 * Set the value of the relation's relhassubclass field in pg_class.
3563 * It's always safe to set this field to true, because all SQL commands are
3564 * ready to see true and then find no children. On the other hand, commands
3565 * generally assume zero children if this is false.
3567 * Caller must hold any self-exclusive lock until end of transaction. If the
3568 * new value is false, caller must have acquired that lock before reading the
3569 * evidence that justified the false value. That way, it properly waits if
3570 * another backend is simultaneously concluding no need to change the tuple
3571 * (new and old values are true).
3573 * NOTE: an important side-effect of this operation is that an SI invalidation
3574 * message is sent out to all backends --- including me --- causing plans
3575 * referencing the relation to be rebuilt with the new list of children.
3576 * This must happen even if we find that no change is needed in the pg_class
3580 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3582 Relation relationRelation
;
3584 Form_pg_class classtuple
;
3586 Assert(CheckRelationOidLockedByMe(relationId
,
3587 ShareUpdateExclusiveLock
, false) ||
3588 CheckRelationOidLockedByMe(relationId
,
3589 ShareRowExclusiveLock
, true));
3592 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3594 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3595 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3596 if (!HeapTupleIsValid(tuple
))
3597 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3598 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3600 if (classtuple
->relhassubclass
!= relhassubclass
)
3602 classtuple
->relhassubclass
= relhassubclass
;
3603 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3607 /* no need to change tuple, but force relcache rebuild anyway */
3608 CacheInvalidateRelcacheByTuple(tuple
);
3611 heap_freetuple(tuple
);
3612 table_close(relationRelation
, RowExclusiveLock
);
3616 * CheckRelationTableSpaceMove
3617 * Check if relation can be moved to new tablespace.
3619 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3621 * Returns true if the relation can be moved to the new tablespace; raises
3622 * an error if it is not possible to do the move; returns false if the move
3623 * would have no effect.
3626 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3628 Oid oldTableSpaceId
;
3631 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3634 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3635 if (newTableSpaceId
== oldTableSpaceId
||
3636 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3640 * We cannot support moving mapped relations into different tablespaces.
3641 * (In particular this eliminates all shared catalogs.)
3643 if (RelationIsMapped(rel
))
3645 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3646 errmsg("cannot move system relation \"%s\"",
3647 RelationGetRelationName(rel
))));
3649 /* Cannot move a non-shared relation into pg_global */
3650 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3652 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3653 errmsg("only shared relations can be placed in pg_global tablespace")));
3656 * Do not allow moving temp tables of other backends ... their local
3657 * buffer manager is not going to cope.
3659 if (RELATION_IS_OTHER_TEMP(rel
))
3661 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3662 errmsg("cannot move temporary tables of other sessions")));
3668 * SetRelationTableSpace
3669 * Set new reltablespace and relfilenumber in pg_class entry.
3671 * newTableSpaceId is the new tablespace for the relation, and
3672 * newRelFilenumber its new filenumber. If newRelFilenumber is
3673 * InvalidRelFileNumber, this field is not updated.
3675 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3677 * The caller of this routine had better check if a relation can be
3678 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3679 * first, and is responsible for making the change visible with
3680 * CommandCounterIncrement().
3683 SetRelationTableSpace(Relation rel
,
3684 Oid newTableSpaceId
,
3685 RelFileNumber newRelFilenumber
)
3689 ItemPointerData otid
;
3690 Form_pg_class rd_rel
;
3691 Oid reloid
= RelationGetRelid(rel
);
3693 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3695 /* Get a modifiable copy of the relation's pg_class row. */
3696 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3698 tuple
= SearchSysCacheLockedCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3699 if (!HeapTupleIsValid(tuple
))
3700 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3701 otid
= tuple
->t_self
;
3702 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3704 /* Update the pg_class row. */
3705 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3706 InvalidOid
: newTableSpaceId
;
3707 if (RelFileNumberIsValid(newRelFilenumber
))
3708 rd_rel
->relfilenode
= newRelFilenumber
;
3709 CatalogTupleUpdate(pg_class
, &otid
, tuple
);
3710 UnlockTuple(pg_class
, &otid
, InplaceUpdateTupleLock
);
3713 * Record dependency on tablespace. This is only required for relations
3714 * that have no physical storage.
3716 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3717 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3718 rd_rel
->reltablespace
);
3720 heap_freetuple(tuple
);
3721 table_close(pg_class
, RowExclusiveLock
);
3725 * renameatt_check - basic sanity checks before attribute rename
3728 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3730 char relkind
= classform
->relkind
;
3732 if (classform
->reloftype
&& !recursing
)
3734 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3735 errmsg("cannot rename column of typed table")));
3738 * Renaming the columns of sequences or toast tables doesn't actually
3739 * break anything from the system's point of view, since internal
3740 * references are by attnum. But it doesn't seem right to allow users to
3741 * change names that are hardcoded into the system, hence the following
3744 if (relkind
!= RELKIND_RELATION
&&
3745 relkind
!= RELKIND_VIEW
&&
3746 relkind
!= RELKIND_MATVIEW
&&
3747 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3748 relkind
!= RELKIND_INDEX
&&
3749 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3750 relkind
!= RELKIND_FOREIGN_TABLE
&&
3751 relkind
!= RELKIND_PARTITIONED_TABLE
)
3753 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3754 errmsg("cannot rename columns of relation \"%s\"",
3755 NameStr(classform
->relname
)),
3756 errdetail_relkind_not_supported(relkind
)));
3759 * permissions checking. only the owner of a class can change its schema.
3761 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3762 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3763 NameStr(classform
->relname
));
3764 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3766 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3767 errmsg("permission denied: \"%s\" is a system catalog",
3768 NameStr(classform
->relname
))));
3772 * renameatt_internal - workhorse for renameatt
3774 * Return value is the attribute number in the 'myrelid' relation.
3777 renameatt_internal(Oid myrelid
,
3778 const char *oldattname
,
3779 const char *newattname
,
3782 int expected_parents
,
3783 DropBehavior behavior
)
3785 Relation targetrelation
;
3786 Relation attrelation
;
3788 Form_pg_attribute attform
;
3792 * Grab an exclusive lock on the target table, which we will NOT release
3793 * until end of transaction.
3795 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3796 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3799 * if the 'recurse' flag is set then we are supposed to rename this
3800 * attribute in all classes that inherit from 'relname' (as well as in
3803 * any permissions or problems with duplicate attributes will cause the
3804 * whole transaction to abort, which is what we want -- all or nothing.
3814 * we need the number of parents for each child so that the recursive
3815 * calls to renameatt() can determine whether there are any parents
3816 * outside the inheritance hierarchy being processed.
3818 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3822 * find_all_inheritors does the recursive search of the inheritance
3823 * hierarchy, so all we have to do is process all of the relids in the
3824 * list that it returns.
3826 forboth(lo
, child_oids
, li
, child_numparents
)
3828 Oid childrelid
= lfirst_oid(lo
);
3829 int numparents
= lfirst_int(li
);
3831 if (childrelid
== myrelid
)
3833 /* note we need not recurse again */
3834 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3840 * If we are told not to recurse, there had better not be any child
3841 * tables; else the rename would put them out of step.
3843 * expected_parents will only be 0 if we are not already recursing.
3845 if (expected_parents
== 0 &&
3846 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3848 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3849 errmsg("inherited column \"%s\" must be renamed in child tables too",
3853 /* rename attributes in typed tables of composite type */
3854 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3859 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3860 RelationGetRelationName(targetrelation
),
3863 foreach(lo
, child_oids
)
3864 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3867 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3869 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3870 if (!HeapTupleIsValid(atttup
))
3872 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3873 errmsg("column \"%s\" does not exist",
3875 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3877 attnum
= attform
->attnum
;
3880 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3881 errmsg("cannot rename system column \"%s\"",
3885 * if the attribute is inherited, forbid the renaming. if this is a
3886 * top-level call to renameatt(), then expected_parents will be 0, so the
3887 * effect of this code will be to prohibit the renaming if the attribute
3888 * is inherited at all. if this is a recursive call to renameatt(),
3889 * expected_parents will be the number of parents the current relation has
3890 * within the inheritance hierarchy being processed, so we'll prohibit the
3891 * renaming only if there are additional parents from elsewhere.
3893 if (attform
->attinhcount
> expected_parents
)
3895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3896 errmsg("cannot rename inherited column \"%s\"",
3899 /* new name should not already exist */
3900 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3902 /* apply the update */
3903 namestrcpy(&(attform
->attname
), newattname
);
3905 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3907 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3909 heap_freetuple(atttup
);
3911 table_close(attrelation
, RowExclusiveLock
);
3913 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3919 * Perform permissions and integrity checks before acquiring a relation lock.
3922 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3928 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3929 if (!HeapTupleIsValid(tuple
))
3930 return; /* concurrently dropped */
3931 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3932 renameatt_check(relid
, form
, false);
3933 ReleaseSysCache(tuple
);
3937 * renameatt - changes the name of an attribute in a relation
3939 * The returned ObjectAddress is that of the renamed column.
3942 renameatt(RenameStmt
*stmt
)
3946 ObjectAddress address
;
3948 /* lock level taken here should match renameatt_internal */
3949 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3950 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3951 RangeVarCallbackForRenameAttribute
,
3954 if (!OidIsValid(relid
))
3957 (errmsg("relation \"%s\" does not exist, skipping",
3958 stmt
->relation
->relname
)));
3959 return InvalidObjectAddress
;
3963 renameatt_internal(relid
,
3964 stmt
->subname
, /* old att name */
3965 stmt
->newname
, /* new att name */
3966 stmt
->relation
->inh
, /* recursive? */
3967 false, /* recursing? */
3968 0, /* expected inhcount */
3971 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3977 * same logic as renameatt_internal
3979 static ObjectAddress
3980 rename_constraint_internal(Oid myrelid
,
3982 const char *oldconname
,
3983 const char *newconname
,
3986 int expected_parents
)
3988 Relation targetrelation
= NULL
;
3991 Form_pg_constraint con
;
3992 ObjectAddress address
;
3994 Assert(!myrelid
|| !mytypid
);
3998 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
4002 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
4005 * don't tell it whether we're recursing; we allow changing typed
4008 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
4010 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
4013 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
4014 if (!HeapTupleIsValid(tuple
))
4015 elog(ERROR
, "cache lookup failed for constraint %u",
4017 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
4020 (con
->contype
== CONSTRAINT_CHECK
||
4021 con
->contype
== CONSTRAINT_NOTNULL
) &&
4031 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
4034 forboth(lo
, child_oids
, li
, child_numparents
)
4036 Oid childrelid
= lfirst_oid(lo
);
4037 int numparents
= lfirst_int(li
);
4039 if (childrelid
== myrelid
)
4042 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
4047 if (expected_parents
== 0 &&
4048 find_inheritance_children(myrelid
, NoLock
) != NIL
)
4050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4051 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4055 if (con
->coninhcount
> expected_parents
)
4057 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4058 errmsg("cannot rename inherited constraint \"%s\"",
4063 && (con
->contype
== CONSTRAINT_PRIMARY
4064 || con
->contype
== CONSTRAINT_UNIQUE
4065 || con
->contype
== CONSTRAINT_EXCLUSION
))
4066 /* rename the index; this renames the constraint as well */
4067 RenameRelationInternal(con
->conindid
, newconname
, false, true);
4069 RenameConstraintById(constraintOid
, newconname
);
4071 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
4073 ReleaseSysCache(tuple
);
4078 * Invalidate relcache so as others can see the new constraint name.
4080 CacheInvalidateRelcache(targetrelation
);
4082 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
4089 RenameConstraint(RenameStmt
*stmt
)
4091 Oid relid
= InvalidOid
;
4092 Oid typid
= InvalidOid
;
4094 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
4099 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
4100 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
4101 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
4102 if (!HeapTupleIsValid(tup
))
4103 elog(ERROR
, "cache lookup failed for type %u", typid
);
4104 checkDomainOwner(tup
);
4105 ReleaseSysCache(tup
);
4106 table_close(rel
, NoLock
);
4110 /* lock level taken here should match rename_constraint_internal */
4111 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
4112 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4113 RangeVarCallbackForRenameAttribute
,
4115 if (!OidIsValid(relid
))
4118 (errmsg("relation \"%s\" does not exist, skipping",
4119 stmt
->relation
->relname
)));
4120 return InvalidObjectAddress
;
4125 rename_constraint_internal(relid
, typid
,
4129 stmt
->relation
->inh
), /* recursive? */
4130 false, /* recursing? */
4131 0 /* expected inhcount */ );
4135 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4139 RenameRelation(RenameStmt
*stmt
)
4141 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
4143 ObjectAddress address
;
4146 * Grab an exclusive lock on the target table, index, sequence, view,
4147 * materialized view, or foreign table, which we will NOT release until
4148 * end of transaction.
4150 * Lock level used here should match RenameRelationInternal, to avoid lock
4151 * escalation. However, because ALTER INDEX can be used with any relation
4152 * type, we mustn't believe without verification.
4160 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
4162 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4163 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4164 RangeVarCallbackForAlterRelation
,
4167 if (!OidIsValid(relid
))
4170 (errmsg("relation \"%s\" does not exist, skipping",
4171 stmt
->relation
->relname
)));
4172 return InvalidObjectAddress
;
4176 * We allow mismatched statement and object types (e.g., ALTER INDEX
4177 * to rename a table), but we might've used the wrong lock level. If
4178 * that happens, retry with the correct lock level. We don't bother
4179 * if we already acquired AccessExclusiveLock with an index, however.
4181 relkind
= get_rel_relkind(relid
);
4182 obj_is_index
= (relkind
== RELKIND_INDEX
||
4183 relkind
== RELKIND_PARTITIONED_INDEX
);
4184 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
4187 UnlockRelationOid(relid
, lockmode
);
4188 is_index_stmt
= obj_is_index
;
4192 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
4194 ObjectAddressSet(address
, RelationRelationId
, relid
);
4200 * RenameRelationInternal - change the name of a relation
4203 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
4205 Relation targetrelation
;
4206 Relation relrelation
; /* for RELATION relation */
4207 ItemPointerData otid
;
4209 Form_pg_class relform
;
4213 * Grab a lock on the target relation, which we will NOT release until end
4214 * of transaction. We need at least a self-exclusive lock so that
4215 * concurrent DDL doesn't overwrite the rename if they start updating
4216 * while still seeing the old version. The lock also guards against
4217 * triggering relcache reloads in concurrent sessions, which might not
4218 * handle this information changing under them. For indexes, we can use a
4219 * reduced lock level because RelationReloadIndexInfo() handles indexes
4222 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
4223 namespaceId
= RelationGetNamespace(targetrelation
);
4226 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4228 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4230 reltup
= SearchSysCacheLockedCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4231 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4232 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4233 otid
= reltup
->t_self
;
4234 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4236 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
4238 (errcode(ERRCODE_DUPLICATE_TABLE
),
4239 errmsg("relation \"%s\" already exists",
4243 * RenameRelation is careful not to believe the caller's idea of the
4244 * relation kind being handled. We don't have to worry about this, but
4245 * let's not be totally oblivious to it. We can process an index as
4246 * not-an-index, but not the other way around.
4249 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4250 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4253 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4254 * because it's a copy...)
4256 namestrcpy(&(relform
->relname
), newrelname
);
4258 CatalogTupleUpdate(relrelation
, &otid
, reltup
);
4259 UnlockTuple(relrelation
, &otid
, InplaceUpdateTupleLock
);
4261 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4262 InvalidOid
, is_internal
);
4264 heap_freetuple(reltup
);
4265 table_close(relrelation
, RowExclusiveLock
);
4268 * Also rename the associated type, if any.
4270 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4271 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4272 newrelname
, namespaceId
);
4275 * Also rename the associated constraint, if any.
4277 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4278 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4280 Oid constraintId
= get_index_constraint(myrelid
);
4282 if (OidIsValid(constraintId
))
4283 RenameConstraintById(constraintId
, newrelname
);
4287 * Close rel, but keep lock!
4289 relation_close(targetrelation
, NoLock
);
4293 * ResetRelRewrite - reset relrewrite
4296 ResetRelRewrite(Oid myrelid
)
4298 Relation relrelation
; /* for RELATION relation */
4300 Form_pg_class relform
;
4303 * Find relation's pg_class tuple.
4305 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4307 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4308 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4309 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4310 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4313 * Update pg_class tuple.
4315 relform
->relrewrite
= InvalidOid
;
4317 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4319 heap_freetuple(reltup
);
4320 table_close(relrelation
, RowExclusiveLock
);
4324 * Disallow ALTER TABLE (and similar commands) when the current backend has
4325 * any open reference to the target table besides the one just acquired by
4326 * the calling command; this implies there's an open cursor or active plan.
4327 * We need this check because our lock doesn't protect us against stomping
4328 * on our own foot, only other people's feet!
4330 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4331 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4332 * possibly be relaxed to only error out for certain types of alterations.
4333 * But the use-case for allowing any of these things is not obvious, so we
4334 * won't work hard at it for now.
4336 * We also reject these commands if there are any pending AFTER trigger events
4337 * for the rel. This is certainly necessary for the rewriting variants of
4338 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4339 * events would try to fetch the wrong tuples. It might be overly cautious
4340 * in other cases, but again it seems better to err on the side of paranoia.
4342 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4343 * we are worried about active indexscans on the index. The trigger-event
4344 * check can be skipped, since we are doing no damage to the parent table.
4346 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4349 CheckTableNotInUse(Relation rel
, const char *stmt
)
4351 int expected_refcnt
;
4353 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4354 if (rel
->rd_refcnt
!= expected_refcnt
)
4356 (errcode(ERRCODE_OBJECT_IN_USE
),
4357 /* translator: first %s is a SQL command, eg ALTER TABLE */
4358 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4359 stmt
, RelationGetRelationName(rel
))));
4361 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4362 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4363 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4365 (errcode(ERRCODE_OBJECT_IN_USE
),
4366 /* translator: first %s is a SQL command, eg ALTER TABLE */
4367 errmsg("cannot %s \"%s\" because it has pending trigger events",
4368 stmt
, RelationGetRelationName(rel
))));
4372 * CheckAlterTableIsSafe
4373 * Verify that it's safe to allow ALTER TABLE on this relation.
4375 * This consists of CheckTableNotInUse() plus a check that the relation
4376 * isn't another session's temp table. We must split out the temp-table
4377 * check because there are callers of CheckTableNotInUse() that don't want
4378 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4379 * an orphaned temp schema.) Compare truncate_check_activity().
4382 CheckAlterTableIsSafe(Relation rel
)
4385 * Don't allow ALTER on temp tables of other backends. Their local buffer
4386 * manager is not going to cope if we need to change the table's contents.
4387 * Even if we don't, there may be optimizations that assume temp tables
4388 * aren't subject to such interference.
4390 if (RELATION_IS_OTHER_TEMP(rel
))
4392 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4393 errmsg("cannot alter temporary tables of other sessions")));
4396 * Also check for active uses of the relation in the current transaction,
4397 * including open scans and pending AFTER trigger events.
4399 CheckTableNotInUse(rel
, "ALTER TABLE");
4403 * AlterTableLookupRelation
4404 * Look up, and lock, the OID for the relation named by an alter table
4408 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4410 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4411 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4412 RangeVarCallbackForAlterRelation
,
4418 * Execute ALTER TABLE, which can be a list of subcommands
4420 * ALTER TABLE is performed in three phases:
4421 * 1. Examine subcommands and perform pre-transformation checking.
4422 * 2. Validate and transform subcommands, and update system catalogs.
4423 * 3. Scan table(s) to check new constraints, and optionally recopy
4424 * the data into new table(s).
4425 * Phase 3 is not performed unless one or more of the subcommands requires
4426 * it. The intention of this design is to allow multiple independent
4427 * updates of the table schema to be performed with only one pass over the
4430 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4431 * each table to be affected (there may be multiple affected tables if the
4432 * commands traverse a table inheritance hierarchy). Also we do preliminary
4433 * validation of the subcommands. Because earlier subcommands may change
4434 * the catalog state seen by later commands, there are limits to what can
4435 * be done in this phase. Generally, this phase acquires table locks,
4436 * checks permissions and relkind, and recurses to find child tables.
4438 * ATRewriteCatalogs performs phase 2 for each affected table.
4439 * Certain subcommands need to be performed before others to avoid
4440 * unnecessary conflicts; for example, DROP COLUMN should come before
4441 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4442 * lists, one for each logical "pass" of phase 2.
4444 * ATRewriteTables performs phase 3 for those tables that need it.
4446 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4447 * since phase 1 already does it. However, for certain subcommand types
4448 * it is only possible to determine how to recurse at phase 2 time; for
4449 * those cases, phase 1 sets the cmd->recurse flag.
4451 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4452 * the whole operation; we don't have to do anything special to clean up.
4454 * The caller must lock the relation, with an appropriate lock level
4455 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4456 * or higher. We pass the lock level down
4457 * so that we can apply it recursively to inherited tables. Note that the
4458 * lock level we want as we recurse might well be higher than required for
4459 * that specific subcommand. So we pass down the overall lock requirement,
4460 * rather than reassess it at lower levels.
4462 * The caller also provides a "context" which is to be passed back to
4463 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4464 * Some of the fields therein, such as the relid, are used here as well.
4467 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4468 AlterTableUtilityContext
*context
)
4472 /* Caller is required to provide an adequate lock. */
4473 rel
= relation_open(context
->relid
, NoLock
);
4475 CheckAlterTableIsSafe(rel
);
4477 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4481 * AlterTableInternal
4483 * ALTER TABLE with target specified by OID
4485 * We do not reject if the relation is already open, because it's quite
4486 * likely that one or more layers of caller have it open. That means it
4487 * is unsafe to use this entry point for alterations that could break
4488 * existing query plans. On the assumption it's not used for such, we
4489 * don't have to reject pending AFTER triggers, either.
4491 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4492 * used for any subcommand types that require parse transformation or
4493 * could generate subcommands that have to be passed to ProcessUtility.
4496 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4499 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4501 rel
= relation_open(relid
, lockmode
);
4503 EventTriggerAlterTableRelid(relid
);
4505 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4509 * AlterTableGetLockLevel
4511 * Sets the overall lock level required for the supplied list of subcommands.
4512 * Policy for doing this set according to needs of AlterTable(), see
4513 * comments there for overall explanation.
4515 * Function is called before and after parsing, so it must give same
4516 * answer each time it is called. Some subcommands are transformed
4517 * into other subcommand types, so the transform must never be made to a
4518 * lower lock level than previously assigned. All transforms are noted below.
4520 * Since this is called before we lock the table we cannot use table metadata
4521 * to influence the type of lock we acquire.
4523 * There should be no lockmodes hardcoded into the subcommand functions. All
4524 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4525 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4526 * and does not travel through this section of code and cannot be combined with
4527 * any of the subcommands given here.
4529 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4530 * so any changes that might affect SELECTs running on standbys need to use
4531 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4532 * have a solution for that also.
4534 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4535 * that takes a lock less than AccessExclusiveLock can change object definitions
4536 * while pg_dump is running. Be careful to check that the appropriate data is
4537 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4538 * otherwise we might end up with an inconsistent dump that can't restore.
4541 AlterTableGetLockLevel(List
*cmds
)
4544 * This only works if we read catalog tables using MVCC snapshots.
4547 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4551 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4552 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4554 switch (cmd
->subtype
)
4557 * These subcommands rewrite the heap, so require full locks.
4559 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4561 case AT_SetAccessMethod
: /* must rewrite heap */
4562 case AT_SetTableSpace
: /* must rewrite heap */
4563 case AT_AlterColumnType
: /* must rewrite heap */
4564 cmd_lockmode
= AccessExclusiveLock
;
4568 * These subcommands may require addition of toast tables. If
4569 * we add a toast table to a table currently being scanned, we
4570 * might miss data added to the new toast table by concurrent
4571 * insert transactions.
4573 case AT_SetStorage
: /* may add toast tables, see
4574 * ATRewriteCatalogs() */
4575 cmd_lockmode
= AccessExclusiveLock
;
4579 * Removing constraints can affect SELECTs that have been
4580 * optimized assuming the constraint holds true. See also
4581 * CloneFkReferenced.
4583 case AT_DropConstraint
: /* as DROP INDEX */
4584 case AT_DropNotNull
: /* may change some SQL plans */
4585 cmd_lockmode
= AccessExclusiveLock
;
4589 * Subcommands that may be visible to concurrent SELECTs
4591 case AT_DropColumn
: /* change visible to SELECT */
4592 case AT_AddColumnToView
: /* CREATE VIEW */
4593 case AT_DropOids
: /* used to equiv to DropColumn */
4594 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4595 case AT_EnableReplicaRule
: /* may change SELECT rules */
4596 case AT_EnableRule
: /* may change SELECT rules */
4597 case AT_DisableRule
: /* may change SELECT rules */
4598 cmd_lockmode
= AccessExclusiveLock
;
4602 * Changing owner may remove implicit SELECT privileges
4604 case AT_ChangeOwner
: /* change visible to SELECT */
4605 cmd_lockmode
= AccessExclusiveLock
;
4609 * Changing foreign table options may affect optimization.
4611 case AT_GenericOptions
:
4612 case AT_AlterColumnGenericOptions
:
4613 cmd_lockmode
= AccessExclusiveLock
;
4617 * These subcommands affect write operations only.
4620 case AT_EnableAlwaysTrig
:
4621 case AT_EnableReplicaTrig
:
4622 case AT_EnableTrigAll
:
4623 case AT_EnableTrigUser
:
4624 case AT_DisableTrig
:
4625 case AT_DisableTrigAll
:
4626 case AT_DisableTrigUser
:
4627 cmd_lockmode
= ShareRowExclusiveLock
;
4631 * These subcommands affect write operations only. XXX
4632 * Theoretically, these could be ShareRowExclusiveLock.
4634 case AT_ColumnDefault
:
4635 case AT_CookedColumnDefault
:
4636 case AT_AlterConstraint
:
4637 case AT_AddIndex
: /* from ADD CONSTRAINT */
4638 case AT_AddIndexConstraint
:
4639 case AT_ReplicaIdentity
:
4641 case AT_EnableRowSecurity
:
4642 case AT_DisableRowSecurity
:
4643 case AT_ForceRowSecurity
:
4644 case AT_NoForceRowSecurity
:
4645 case AT_AddIdentity
:
4646 case AT_DropIdentity
:
4647 case AT_SetIdentity
:
4648 case AT_SetExpression
:
4649 case AT_DropExpression
:
4650 case AT_SetCompression
:
4651 cmd_lockmode
= AccessExclusiveLock
;
4654 case AT_AddConstraint
:
4655 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4656 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4657 if (IsA(cmd
->def
, Constraint
))
4659 Constraint
*con
= (Constraint
*) cmd
->def
;
4661 switch (con
->contype
)
4663 case CONSTR_EXCLUSION
:
4664 case CONSTR_PRIMARY
:
4668 * Cases essentially the same as CREATE INDEX. We
4669 * could reduce the lock strength to ShareLock if
4670 * we can work out how to allow concurrent catalog
4671 * updates. XXX Might be set down to
4672 * ShareRowExclusiveLock but requires further
4675 cmd_lockmode
= AccessExclusiveLock
;
4677 case CONSTR_FOREIGN
:
4680 * We add triggers to both tables when we add a
4681 * Foreign Key, so the lock level must be at least
4682 * as strong as CREATE TRIGGER.
4684 cmd_lockmode
= ShareRowExclusiveLock
;
4688 cmd_lockmode
= AccessExclusiveLock
;
4694 * These subcommands affect inheritance behaviour. Queries
4695 * started before us will continue to see the old inheritance
4696 * behaviour, while queries started after we commit will see
4697 * new behaviour. No need to prevent reads or writes to the
4698 * subtable while we hook it up though. Changing the TupDesc
4699 * may be a problem, so keep highest lock.
4702 case AT_DropInherit
:
4703 cmd_lockmode
= AccessExclusiveLock
;
4707 * These subcommands affect implicit row type conversion. They
4708 * have affects similar to CREATE/DROP CAST on queries. don't
4709 * provide for invalidating parse trees as a result of such
4710 * changes, so we keep these at AccessExclusiveLock.
4714 cmd_lockmode
= AccessExclusiveLock
;
4718 * Only used by CREATE OR REPLACE VIEW which must conflict
4719 * with an SELECTs currently using the view.
4721 case AT_ReplaceRelOptions
:
4722 cmd_lockmode
= AccessExclusiveLock
;
4726 * These subcommands affect general strategies for performance
4727 * and maintenance, though don't change the semantic results
4728 * from normal data reads and writes. Delaying an ALTER TABLE
4729 * behind currently active writes only delays the point where
4730 * the new strategy begins to take effect, so there is no
4731 * benefit in waiting. In this case the minimum restriction
4732 * applies: we don't currently allow concurrent catalog
4735 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4736 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4737 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4738 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4739 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4740 cmd_lockmode
= ShareUpdateExclusiveLock
;
4744 case AT_SetUnLogged
:
4745 cmd_lockmode
= AccessExclusiveLock
;
4748 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4749 cmd_lockmode
= ShareUpdateExclusiveLock
;
4753 * Rel options are more complex than first appears. Options
4754 * are set here for tables, views and indexes; for historical
4755 * reasons these can all be used with ALTER TABLE, so we can't
4756 * decide between them using the basic grammar.
4758 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4760 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4762 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4765 case AT_AttachPartition
:
4766 cmd_lockmode
= ShareUpdateExclusiveLock
;
4769 case AT_DetachPartition
:
4770 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4771 cmd_lockmode
= ShareUpdateExclusiveLock
;
4773 cmd_lockmode
= AccessExclusiveLock
;
4776 case AT_DetachPartitionFinalize
:
4777 cmd_lockmode
= ShareUpdateExclusiveLock
;
4781 elog(ERROR
, "unrecognized alter table type: %d",
4782 (int) cmd
->subtype
);
4787 * Take the greatest lockmode from any subcommand
4789 if (cmd_lockmode
> lockmode
)
4790 lockmode
= cmd_lockmode
;
4797 * ATController provides top level control over the phases.
4799 * parsetree is passed in to allow it to be passed to event triggers
4803 ATController(AlterTableStmt
*parsetree
,
4804 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4805 AlterTableUtilityContext
*context
)
4810 /* Phase 1: preliminary examination of commands, create work queue */
4813 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4815 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4818 /* Close the relation, but keep lock until commit */
4819 relation_close(rel
, NoLock
);
4821 /* Phase 2: update system catalogs */
4822 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4824 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4825 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4831 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4832 * recursion and permission checks.
4834 * Caller must have acquired appropriate lock type on relation already.
4835 * This lock should be held until commit.
4838 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4839 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4840 AlterTableUtilityContext
*context
)
4842 AlteredTableInfo
*tab
;
4843 AlterTablePass pass
= AT_PASS_UNSET
;
4845 /* Find or create work queue entry for this table */
4846 tab
= ATGetQueueEntry(wqueue
, rel
);
4849 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4850 * partitions that are pending detach.
4852 if (rel
->rd_rel
->relispartition
&&
4853 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4854 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4856 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4857 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4858 RelationGetRelationName(rel
)),
4859 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4862 * Copy the original subcommand for each table, so we can scribble on it.
4863 * This avoids conflicts when different child tables need to make
4864 * different parse transformations (for example, the same column may have
4865 * different column numbers in different children).
4867 cmd
= copyObject(cmd
);
4870 * Do permissions and relkind checking, recursion to child tables if
4871 * needed, and any additional phase-1 processing needed. (But beware of
4872 * adding any processing that looks at table details that another
4873 * subcommand could change. In some cases we reject multiple subcommands
4874 * that could try to change the same state in contrary ways.)
4876 switch (cmd
->subtype
)
4878 case AT_AddColumn
: /* ADD COLUMN */
4879 ATSimplePermissions(cmd
->subtype
, rel
,
4880 ATT_TABLE
| ATT_PARTITIONED_TABLE
|
4881 ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4882 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4884 /* Recursion occurs during execution phase */
4885 pass
= AT_PASS_ADD_COL
;
4887 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4888 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4889 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4891 /* Recursion occurs during execution phase */
4892 pass
= AT_PASS_ADD_COL
;
4894 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4897 * We allow defaults on views so that INSERT into a view can have
4898 * default-ish behavior. This works because the rewriter
4899 * substitutes default values into INSERTs before it expands
4902 ATSimplePermissions(cmd
->subtype
, rel
,
4903 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_VIEW
|
4905 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4906 /* No command-specific prep needed */
4907 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4909 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4910 /* This is currently used only in CREATE TABLE */
4911 /* (so the permission check really isn't necessary) */
4912 ATSimplePermissions(cmd
->subtype
, rel
,
4913 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
4914 /* This command never recurses */
4915 pass
= AT_PASS_ADD_OTHERCONSTR
;
4917 case AT_AddIdentity
:
4918 ATSimplePermissions(cmd
->subtype
, rel
,
4919 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_VIEW
|
4921 /* Set up recursion for phase 2; no other prep needed */
4923 cmd
->recurse
= true;
4924 pass
= AT_PASS_ADD_OTHERCONSTR
;
4926 case AT_SetIdentity
:
4927 ATSimplePermissions(cmd
->subtype
, rel
,
4928 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_VIEW
|
4930 /* Set up recursion for phase 2; no other prep needed */
4932 cmd
->recurse
= true;
4933 /* This should run after AddIdentity, so do it in MISC pass */
4934 pass
= AT_PASS_MISC
;
4936 case AT_DropIdentity
:
4937 ATSimplePermissions(cmd
->subtype
, rel
,
4938 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_VIEW
|
4940 /* Set up recursion for phase 2; no other prep needed */
4942 cmd
->recurse
= true;
4943 pass
= AT_PASS_DROP
;
4945 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4946 ATSimplePermissions(cmd
->subtype
, rel
,
4947 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
4948 /* Set up recursion for phase 2; no other prep needed */
4950 cmd
->recurse
= true;
4951 pass
= AT_PASS_DROP
;
4953 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4954 ATSimplePermissions(cmd
->subtype
, rel
,
4955 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
4956 /* Set up recursion for phase 2; no other prep needed */
4958 cmd
->recurse
= true;
4959 pass
= AT_PASS_COL_ATTRS
;
4961 case AT_SetExpression
: /* ALTER COLUMN SET EXPRESSION */
4962 ATSimplePermissions(cmd
->subtype
, rel
,
4963 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
4964 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4965 pass
= AT_PASS_SET_EXPRESSION
;
4967 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4968 ATSimplePermissions(cmd
->subtype
, rel
,
4969 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
4970 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4971 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4972 pass
= AT_PASS_DROP
;
4974 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4975 ATSimplePermissions(cmd
->subtype
, rel
,
4976 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_MATVIEW
|
4977 ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4978 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4979 /* No command-specific prep needed */
4980 pass
= AT_PASS_MISC
;
4982 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4983 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4984 ATSimplePermissions(cmd
->subtype
, rel
,
4985 ATT_TABLE
| ATT_PARTITIONED_TABLE
|
4986 ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4987 /* This command never recurses */
4988 pass
= AT_PASS_MISC
;
4990 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4991 ATSimplePermissions(cmd
->subtype
, rel
,
4992 ATT_TABLE
| ATT_PARTITIONED_TABLE
|
4993 ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4994 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4995 /* No command-specific prep needed */
4996 pass
= AT_PASS_MISC
;
4998 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4999 ATSimplePermissions(cmd
->subtype
, rel
,
5000 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_MATVIEW
);
5001 /* This command never recurses */
5002 /* No command-specific prep needed */
5003 pass
= AT_PASS_MISC
;
5005 case AT_DropColumn
: /* DROP COLUMN */
5006 ATSimplePermissions(cmd
->subtype
, rel
,
5007 ATT_TABLE
| ATT_PARTITIONED_TABLE
|
5008 ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
5009 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
5011 /* Recursion occurs during execution phase */
5012 pass
= AT_PASS_DROP
;
5014 case AT_AddIndex
: /* ADD INDEX */
5015 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_TABLE
);
5016 /* This command never recurses */
5017 /* No command-specific prep needed */
5018 pass
= AT_PASS_ADD_INDEX
;
5020 case AT_AddConstraint
: /* ADD CONSTRAINT */
5021 ATSimplePermissions(cmd
->subtype
, rel
,
5022 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5023 ATPrepAddPrimaryKey(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
5026 /* recurses at exec time; lock descendants and set flag */
5027 (void) find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
5028 cmd
->recurse
= true;
5030 pass
= AT_PASS_ADD_CONSTR
;
5032 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5033 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_TABLE
);
5034 /* This command never recurses */
5035 /* No command-specific prep needed */
5036 pass
= AT_PASS_ADD_INDEXCONSTR
;
5038 case AT_DropConstraint
: /* DROP CONSTRAINT */
5039 ATSimplePermissions(cmd
->subtype
, rel
,
5040 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5041 ATCheckPartitionsNotInUse(rel
, lockmode
);
5042 /* Other recursion occurs during execution phase */
5043 /* No command-specific prep needed except saving recurse flag */
5045 cmd
->recurse
= true;
5046 pass
= AT_PASS_DROP
;
5048 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5049 ATSimplePermissions(cmd
->subtype
, rel
,
5050 ATT_TABLE
| ATT_PARTITIONED_TABLE
|
5051 ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
5052 /* See comments for ATPrepAlterColumnType */
5053 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
5054 AT_PASS_UNSET
, context
);
5055 Assert(cmd
!= NULL
);
5056 /* Performs own recursion */
5057 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
5059 pass
= AT_PASS_ALTER_TYPE
;
5061 case AT_AlterColumnGenericOptions
:
5062 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5063 /* This command never recurses */
5064 /* No command-specific prep needed */
5065 pass
= AT_PASS_MISC
;
5067 case AT_ChangeOwner
: /* ALTER OWNER */
5068 /* This command never recurses */
5069 /* No command-specific prep needed */
5070 pass
= AT_PASS_MISC
;
5072 case AT_ClusterOn
: /* CLUSTER ON */
5073 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5074 ATSimplePermissions(cmd
->subtype
, rel
,
5075 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_MATVIEW
);
5076 /* These commands never recurse */
5077 /* No command-specific prep needed */
5078 pass
= AT_PASS_MISC
;
5080 case AT_SetLogged
: /* SET LOGGED */
5081 case AT_SetUnLogged
: /* SET UNLOGGED */
5082 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
5083 if (tab
->chgPersistence
)
5085 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5086 errmsg("cannot change persistence setting twice")));
5087 ATPrepChangePersistence(tab
, rel
, cmd
->subtype
== AT_SetLogged
);
5088 pass
= AT_PASS_MISC
;
5090 case AT_DropOids
: /* SET WITHOUT OIDS */
5091 ATSimplePermissions(cmd
->subtype
, rel
,
5092 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5093 pass
= AT_PASS_DROP
;
5095 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5096 ATSimplePermissions(cmd
->subtype
, rel
,
5097 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_MATVIEW
);
5099 /* check if another access method change was already requested */
5100 if (tab
->chgAccessMethod
)
5102 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5103 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5105 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
5106 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
5108 case AT_SetTableSpace
: /* SET TABLESPACE */
5109 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_TABLE
|
5110 ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
);
5111 /* This command never recurses */
5112 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
5113 pass
= AT_PASS_MISC
; /* doesn't actually matter */
5115 case AT_SetRelOptions
: /* SET (...) */
5116 case AT_ResetRelOptions
: /* RESET (...) */
5117 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
5118 ATSimplePermissions(cmd
->subtype
, rel
,
5119 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_VIEW
|
5120 ATT_MATVIEW
| ATT_INDEX
);
5121 /* This command never recurses */
5122 /* No command-specific prep needed */
5123 pass
= AT_PASS_MISC
;
5125 case AT_AddInherit
: /* INHERIT */
5126 ATSimplePermissions(cmd
->subtype
, rel
,
5127 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5128 /* This command never recurses */
5129 ATPrepAddInherit(rel
);
5130 pass
= AT_PASS_MISC
;
5132 case AT_DropInherit
: /* NO INHERIT */
5133 ATSimplePermissions(cmd
->subtype
, rel
,
5134 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5135 /* This command never recurses */
5136 /* No command-specific prep needed */
5137 pass
= AT_PASS_MISC
;
5139 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5140 ATSimplePermissions(cmd
->subtype
, rel
,
5141 ATT_TABLE
| ATT_PARTITIONED_TABLE
);
5142 /* Recursion occurs during execution phase */
5143 pass
= AT_PASS_MISC
;
5145 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5146 ATSimplePermissions(cmd
->subtype
, rel
,
5147 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5148 /* Recursion occurs during execution phase */
5149 /* No command-specific prep needed except saving recurse flag */
5151 cmd
->recurse
= true;
5152 pass
= AT_PASS_MISC
;
5154 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
5155 ATSimplePermissions(cmd
->subtype
, rel
,
5156 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_MATVIEW
);
5157 pass
= AT_PASS_MISC
;
5158 /* This command never recurses */
5159 /* No command-specific prep needed */
5161 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
5162 case AT_EnableAlwaysTrig
:
5163 case AT_EnableReplicaTrig
:
5164 case AT_EnableTrigAll
:
5165 case AT_EnableTrigUser
:
5166 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
5167 case AT_DisableTrigAll
:
5168 case AT_DisableTrigUser
:
5169 ATSimplePermissions(cmd
->subtype
, rel
,
5170 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
5171 /* Set up recursion for phase 2; no other prep needed */
5173 cmd
->recurse
= true;
5174 pass
= AT_PASS_MISC
;
5176 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
5177 case AT_EnableAlwaysRule
:
5178 case AT_EnableReplicaRule
:
5179 case AT_DisableRule
:
5180 case AT_AddOf
: /* OF */
5181 case AT_DropOf
: /* NOT OF */
5182 case AT_EnableRowSecurity
:
5183 case AT_DisableRowSecurity
:
5184 case AT_ForceRowSecurity
:
5185 case AT_NoForceRowSecurity
:
5186 ATSimplePermissions(cmd
->subtype
, rel
,
5187 ATT_TABLE
| ATT_PARTITIONED_TABLE
);
5188 /* These commands never recurse */
5189 /* No command-specific prep needed */
5190 pass
= AT_PASS_MISC
;
5192 case AT_GenericOptions
:
5193 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5194 /* No command-specific prep needed */
5195 pass
= AT_PASS_MISC
;
5197 case AT_AttachPartition
:
5198 ATSimplePermissions(cmd
->subtype
, rel
,
5199 ATT_PARTITIONED_TABLE
| ATT_PARTITIONED_INDEX
);
5200 /* No command-specific prep needed */
5201 pass
= AT_PASS_MISC
;
5203 case AT_DetachPartition
:
5204 ATSimplePermissions(cmd
->subtype
, rel
, ATT_PARTITIONED_TABLE
);
5205 /* No command-specific prep needed */
5206 pass
= AT_PASS_MISC
;
5208 case AT_DetachPartitionFinalize
:
5209 ATSimplePermissions(cmd
->subtype
, rel
, ATT_PARTITIONED_TABLE
);
5210 /* No command-specific prep needed */
5211 pass
= AT_PASS_MISC
;
5214 elog(ERROR
, "unrecognized alter table type: %d",
5215 (int) cmd
->subtype
);
5216 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
5219 Assert(pass
> AT_PASS_UNSET
);
5221 /* Add the subcommand to the appropriate list for phase 2 */
5222 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
5228 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5229 * dispatched in a "safe" execution order (designed to avoid unnecessary
5233 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
5234 AlterTableUtilityContext
*context
)
5239 * We process all the tables "in parallel", one pass at a time. This is
5240 * needed because we may have to propagate work from one table to another
5241 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5242 * re-adding of the foreign key constraint to the other table). Work can
5243 * only be propagated into later passes, however.
5245 for (AlterTablePass pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
5247 /* Go through each table that needs to be processed */
5248 foreach(ltab
, *wqueue
)
5250 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5251 List
*subcmds
= tab
->subcmds
[pass
];
5258 * Open the relation and store it in tab. This allows subroutines
5259 * close and reopen, if necessary. Appropriate lock was obtained
5260 * by phase 1, needn't get it again.
5262 tab
->rel
= relation_open(tab
->relid
, NoLock
);
5264 foreach(lcmd
, subcmds
)
5265 ATExecCmd(wqueue
, tab
,
5266 lfirst_node(AlterTableCmd
, lcmd
),
5267 lockmode
, pass
, context
);
5270 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5271 * (this is not done in ATExecAlterColumnType since it should be
5272 * done only once if multiple columns of a table are altered).
5274 if (pass
== AT_PASS_ALTER_TYPE
|| pass
== AT_PASS_SET_EXPRESSION
)
5275 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
5279 relation_close(tab
->rel
, NoLock
);
5285 /* Check to see if a toast table must be added. */
5286 foreach(ltab
, *wqueue
)
5288 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5291 * If the table is source table of ATTACH PARTITION command, we did
5292 * not modify anything about it that will change its toasting
5293 * requirement, so no need to check.
5295 if (((tab
->relkind
== RELKIND_RELATION
||
5296 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5297 tab
->partition_constraint
== NULL
) ||
5298 tab
->relkind
== RELKIND_MATVIEW
)
5299 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5304 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5307 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5308 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
5309 AlterTableUtilityContext
*context
)
5311 ObjectAddress address
= InvalidObjectAddress
;
5312 Relation rel
= tab
->rel
;
5314 switch (cmd
->subtype
)
5316 case AT_AddColumn
: /* ADD COLUMN */
5317 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5318 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5319 cmd
->recurse
, false,
5320 lockmode
, cur_pass
, context
);
5322 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5323 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5325 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5326 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5328 case AT_AddIdentity
:
5329 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5331 Assert(cmd
!= NULL
);
5332 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5334 case AT_SetIdentity
:
5335 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5337 Assert(cmd
!= NULL
);
5338 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5340 case AT_DropIdentity
:
5341 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
, cmd
->recurse
, false);
5343 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5344 address
= ATExecDropNotNull(rel
, cmd
->name
, cmd
->recurse
, lockmode
);
5346 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5347 address
= ATExecSetNotNull(wqueue
, rel
, NULL
, cmd
->name
,
5348 cmd
->recurse
, false, lockmode
);
5350 case AT_SetExpression
:
5351 address
= ATExecSetExpression(tab
, rel
, cmd
->name
, cmd
->def
, lockmode
);
5353 case AT_DropExpression
:
5354 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5356 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5357 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5359 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5360 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5362 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5363 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5365 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5366 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5368 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5369 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5372 case AT_DropColumn
: /* DROP COLUMN */
5373 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5374 cmd
->behavior
, cmd
->recurse
, false,
5375 cmd
->missing_ok
, lockmode
,
5378 case AT_AddIndex
: /* ADD INDEX */
5379 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5382 case AT_ReAddIndex
: /* ADD INDEX */
5383 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5386 case AT_ReAddStatistics
: /* ADD STATISTICS */
5387 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5390 case AT_AddConstraint
: /* ADD CONSTRAINT */
5391 /* Transform the command only during initial examination */
5392 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5393 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5394 cmd
->recurse
, lockmode
,
5396 /* Depending on constraint type, might be no more work to do now */
5399 ATExecAddConstraint(wqueue
, tab
, rel
,
5400 (Constraint
*) cmd
->def
,
5401 cmd
->recurse
, false, lockmode
);
5403 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5405 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5406 true, true, lockmode
);
5408 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5411 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5412 ((AlterDomainStmt
*) cmd
->def
)->def
,
5415 case AT_ReAddComment
: /* Re-add existing comment */
5416 address
= CommentObject((CommentStmt
*) cmd
->def
);
5418 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5419 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5422 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5423 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5425 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5426 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5429 case AT_DropConstraint
: /* DROP CONSTRAINT */
5430 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5432 cmd
->missing_ok
, lockmode
);
5434 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5435 /* parse transformation was done earlier */
5436 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5438 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5440 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5441 (List
*) cmd
->def
, lockmode
);
5443 case AT_ChangeOwner
: /* ALTER OWNER */
5444 ATExecChangeOwner(RelationGetRelid(rel
),
5445 get_rolespec_oid(cmd
->newowner
, false),
5448 case AT_ClusterOn
: /* CLUSTER ON */
5449 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5451 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5452 ATExecDropCluster(rel
, lockmode
);
5454 case AT_SetLogged
: /* SET LOGGED */
5455 case AT_SetUnLogged
: /* SET UNLOGGED */
5457 case AT_DropOids
: /* SET WITHOUT OIDS */
5458 /* nothing to do here, oid columns don't exist anymore */
5460 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5463 * Only do this for partitioned tables, for which this is just a
5464 * catalog change. Tables with storage are handled by Phase 3.
5466 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
5467 tab
->chgAccessMethod
)
5468 ATExecSetAccessMethodNoStorage(rel
, tab
->newAccessMethod
);
5470 case AT_SetTableSpace
: /* SET TABLESPACE */
5473 * Only do this for partitioned tables and indexes, for which this
5474 * is just a catalog change. Other relation types which have
5475 * storage are handled by Phase 3.
5477 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5478 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5479 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5482 case AT_SetRelOptions
: /* SET (...) */
5483 case AT_ResetRelOptions
: /* RESET (...) */
5484 case AT_ReplaceRelOptions
: /* replace entire option list */
5485 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5487 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5488 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5489 TRIGGER_FIRES_ON_ORIGIN
, false,
5493 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5494 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5495 TRIGGER_FIRES_ALWAYS
, false,
5499 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5500 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5501 TRIGGER_FIRES_ON_REPLICA
, false,
5505 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5506 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5507 TRIGGER_DISABLED
, false,
5511 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5512 ATExecEnableDisableTrigger(rel
, NULL
,
5513 TRIGGER_FIRES_ON_ORIGIN
, false,
5517 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5518 ATExecEnableDisableTrigger(rel
, NULL
,
5519 TRIGGER_DISABLED
, false,
5523 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5524 ATExecEnableDisableTrigger(rel
, NULL
,
5525 TRIGGER_FIRES_ON_ORIGIN
, true,
5529 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5530 ATExecEnableDisableTrigger(rel
, NULL
,
5531 TRIGGER_DISABLED
, true,
5536 case AT_EnableRule
: /* ENABLE RULE name */
5537 ATExecEnableDisableRule(rel
, cmd
->name
,
5538 RULE_FIRES_ON_ORIGIN
, lockmode
);
5540 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5541 ATExecEnableDisableRule(rel
, cmd
->name
,
5542 RULE_FIRES_ALWAYS
, lockmode
);
5544 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5545 ATExecEnableDisableRule(rel
, cmd
->name
,
5546 RULE_FIRES_ON_REPLICA
, lockmode
);
5548 case AT_DisableRule
: /* DISABLE RULE name */
5549 ATExecEnableDisableRule(rel
, cmd
->name
,
5550 RULE_DISABLED
, lockmode
);
5554 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5556 case AT_DropInherit
:
5557 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5560 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5563 ATExecDropOf(rel
, lockmode
);
5565 case AT_ReplicaIdentity
:
5566 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5568 case AT_EnableRowSecurity
:
5569 ATExecSetRowSecurity(rel
, true);
5571 case AT_DisableRowSecurity
:
5572 ATExecSetRowSecurity(rel
, false);
5574 case AT_ForceRowSecurity
:
5575 ATExecForceNoForceRowSecurity(rel
, true);
5577 case AT_NoForceRowSecurity
:
5578 ATExecForceNoForceRowSecurity(rel
, false);
5580 case AT_GenericOptions
:
5581 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5583 case AT_AttachPartition
:
5584 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5586 Assert(cmd
!= NULL
);
5587 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5588 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5591 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5592 ((PartitionCmd
*) cmd
->def
)->name
);
5594 case AT_DetachPartition
:
5595 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5597 Assert(cmd
!= NULL
);
5598 /* ATPrepCmd ensures it must be a table */
5599 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5600 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5601 ((PartitionCmd
*) cmd
->def
)->name
,
5602 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5604 case AT_DetachPartitionFinalize
:
5605 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5608 elog(ERROR
, "unrecognized alter table type: %d",
5609 (int) cmd
->subtype
);
5614 * Report the subcommand to interested event triggers.
5617 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5620 * Bump the command counter to ensure the next subcommand in the sequence
5621 * can see the changes so far
5623 CommandCounterIncrement();
5627 * ATParseTransformCmd: perform parse transformation for one subcommand
5629 * Returns the transformed subcommand tree, if there is one, else NULL.
5631 * The parser may hand back additional AlterTableCmd(s) and/or other
5632 * utility statements, either before or after the original subcommand.
5633 * Other AlterTableCmds are scheduled into the appropriate slot of the
5634 * AlteredTableInfo (they had better be for later passes than the current one).
5635 * Utility statements that are supposed to happen before the AlterTableCmd
5636 * are executed immediately. Those that are supposed to happen afterwards
5637 * are added to the tab->afterStmts list to be done at the very end.
5639 static AlterTableCmd
*
5640 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5641 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5642 AlterTablePass cur_pass
, AlterTableUtilityContext
*context
)
5644 AlterTableCmd
*newcmd
= NULL
;
5645 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5650 /* Gin up an AlterTableStmt with just this subcommand and this table */
5652 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5653 pstrdup(RelationGetRelationName(rel
)),
5655 atstmt
->relation
->inh
= recurse
;
5656 atstmt
->cmds
= list_make1(cmd
);
5657 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5658 atstmt
->missing_ok
= false;
5660 /* Transform the AlterTableStmt */
5661 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5663 context
->queryString
,
5667 /* Execute any statements that should happen before these subcommand(s) */
5668 foreach(lc
, beforeStmts
)
5670 Node
*stmt
= (Node
*) lfirst(lc
);
5672 ProcessUtilityForAlterTable(stmt
, context
);
5673 CommandCounterIncrement();
5676 /* Examine the transformed subcommands and schedule them appropriately */
5677 foreach(lc
, atstmt
->cmds
)
5679 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5680 AlterTablePass pass
;
5683 * This switch need only cover the subcommand types that can be added
5684 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5685 * executing the subcommand immediately, as a substitute for the
5686 * original subcommand. (Note, however, that this does cause
5687 * AT_AddConstraint subcommands to be rescheduled into later passes,
5688 * which is important for index and foreign key constraints.)
5690 * We assume we needn't do any phase-1 checks for added subcommands.
5692 switch (cmd2
->subtype
)
5695 pass
= AT_PASS_ADD_INDEX
;
5697 case AT_AddIndexConstraint
:
5698 pass
= AT_PASS_ADD_INDEXCONSTR
;
5700 case AT_AddConstraint
:
5701 /* Recursion occurs during execution phase */
5703 cmd2
->recurse
= true;
5704 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5706 case CONSTR_NOTNULL
:
5707 pass
= AT_PASS_COL_ATTRS
;
5709 case CONSTR_PRIMARY
:
5711 case CONSTR_EXCLUSION
:
5712 pass
= AT_PASS_ADD_INDEXCONSTR
;
5715 pass
= AT_PASS_ADD_OTHERCONSTR
;
5719 case AT_AlterColumnGenericOptions
:
5720 /* This command never recurses */
5721 /* No command-specific prep needed */
5722 pass
= AT_PASS_MISC
;
5729 if (pass
< cur_pass
)
5731 /* Cannot schedule into a pass we already finished */
5732 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5735 else if (pass
> cur_pass
)
5737 /* OK, queue it up for later */
5738 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5743 * We should see at most one subcommand for the current pass,
5744 * which is the transformed version of the original subcommand.
5746 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5748 /* Found the transformed version of our subcommand */
5752 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5757 /* Queue up any after-statements to happen at the end */
5758 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5764 * ATRewriteTables: ALTER TABLE phase 3
5767 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5768 AlterTableUtilityContext
*context
)
5772 /* Go through each table that needs to be checked or rewritten */
5773 foreach(ltab
, *wqueue
)
5775 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5777 /* Relations without storage may be ignored here */
5778 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5782 * If we change column data types, the operation has to be propagated
5783 * to tables that use this table's rowtype as a column type.
5784 * tab->newvals will also be non-NULL in the case where we're adding a
5785 * column with a default. We choose to forbid that case as well,
5786 * since composite types might eventually support defaults.
5788 * (Eventually we'll probably need to check for composite type
5789 * dependencies even when we're just scanning the table without a
5790 * rewrite, but at the moment a composite type does not enforce any
5791 * constraints, so it's not necessary/appropriate to enforce them just
5794 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5798 rel
= table_open(tab
->relid
, NoLock
);
5799 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5800 table_close(rel
, NoLock
);
5804 * We only need to rewrite the table if at least one column needs to
5805 * be recomputed, or we are changing its persistence or access method.
5807 * There are two reasons for requiring a rewrite when changing
5808 * persistence: on one hand, we need to ensure that the buffers
5809 * belonging to each of the two relations are marked with or without
5810 * BM_PERMANENT properly. On the other hand, since rewriting creates
5811 * and assigns a new relfilenumber, we automatically create or drop an
5812 * init fork for the relation as appropriate.
5814 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5816 /* Build a temporary relation and copy data */
5819 Oid NewAccessMethod
;
5823 OldHeap
= table_open(tab
->relid
, NoLock
);
5826 * We don't support rewriting of system catalogs; there are too
5827 * many corner cases and too little benefit. In particular this
5828 * is certainly not going to work for mapped catalogs.
5830 if (IsSystemRelation(OldHeap
))
5832 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5833 errmsg("cannot rewrite system relation \"%s\"",
5834 RelationGetRelationName(OldHeap
))));
5836 if (RelationIsUsedAsCatalogTable(OldHeap
))
5838 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5839 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5840 RelationGetRelationName(OldHeap
))));
5843 * Don't allow rewrite on temp tables of other backends ... their
5844 * local buffer manager is not going to cope. (This is redundant
5845 * with the check in CheckAlterTableIsSafe, but for safety we'll
5848 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5850 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5851 errmsg("cannot rewrite temporary tables of other sessions")));
5854 * Select destination tablespace (same as original unless user
5855 * requested a change)
5857 if (tab
->newTableSpace
)
5858 NewTableSpace
= tab
->newTableSpace
;
5860 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5863 * Select destination access method (same as original unless user
5864 * requested a change)
5866 if (tab
->chgAccessMethod
)
5867 NewAccessMethod
= tab
->newAccessMethod
;
5869 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5872 * Select persistence of transient table (same as original unless
5873 * user requested a change)
5875 persistence
= tab
->chgPersistence
?
5876 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5878 table_close(OldHeap
, NoLock
);
5881 * Fire off an Event Trigger now, before actually rewriting the
5884 * We don't support Event Trigger for nested commands anywhere,
5885 * here included, and parsetree is given NULL when coming from
5886 * AlterTableInternal.
5888 * And fire it only once.
5891 EventTriggerTableRewrite((Node
*) parsetree
,
5896 * Create transient table that will receive the modified data.
5898 * Ensure it is marked correctly as logged or unlogged. We have
5899 * to do this here so that buffers for the new relfilenumber will
5900 * have the right persistence set, and at the same time ensure
5901 * that the original filenumbers's buffers will get read in with
5902 * the correct setting (i.e. the original one). Otherwise a
5903 * rollback after the rewrite would possibly result with buffers
5904 * for the original filenumbers having the wrong persistence
5907 * NB: This relies on swap_relation_files() also swapping the
5908 * persistence. That wouldn't work for pg_class, but that can't be
5911 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5912 persistence
, lockmode
);
5915 * Copy the heap data into the new table with the desired
5916 * modifications, and test the current data within the table
5917 * against new constraints generated by ALTER TABLE commands.
5919 ATRewriteTable(tab
, OIDNewHeap
);
5922 * Swap the physical files of the old and new heaps, then rebuild
5923 * indexes and discard the old heap. We can use RecentXmin for
5924 * the table's new relfrozenxid because we rewrote all the tuples
5925 * in ATRewriteTable, so no older Xid remains in the table. Also,
5926 * we never try to swap toast tables by content, since we have no
5927 * interest in letting this code work on system catalogs.
5929 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5931 !OidIsValid(tab
->newTableSpace
),
5933 ReadNextMultiXactId(),
5936 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5938 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5940 if (tab
->chgPersistence
)
5941 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5946 * If required, test the current data within the table against new
5947 * constraints generated by ALTER TABLE commands, but don't
5950 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5951 tab
->partition_constraint
!= NULL
)
5952 ATRewriteTable(tab
, InvalidOid
);
5955 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5956 * just do a block-by-block copy.
5958 if (tab
->newTableSpace
)
5959 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5963 * Also change persistence of owned sequences, so that it matches the
5964 * table persistence.
5966 if (tab
->chgPersistence
)
5968 List
*seqlist
= getOwnedSequences(tab
->relid
);
5971 foreach(lc
, seqlist
)
5973 Oid seq_relid
= lfirst_oid(lc
);
5975 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5981 * Foreign key constraints are checked in a final pass, since (a) it's
5982 * generally best to examine each one separately, and (b) it's at least
5983 * theoretically possible that we have changed both relations of the
5984 * foreign key, and we'd better have finished both rewrites before we try
5985 * to read the tables.
5987 foreach(ltab
, *wqueue
)
5989 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5990 Relation rel
= NULL
;
5993 /* Relations without storage may be ignored here too */
5994 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5997 foreach(lcon
, tab
->constraints
)
5999 NewConstraint
*con
= lfirst(lcon
);
6001 if (con
->contype
== CONSTR_FOREIGN
)
6003 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
6008 /* Long since locked, no need for another */
6009 rel
= table_open(tab
->relid
, NoLock
);
6012 refrel
= table_open(con
->refrelid
, RowShareLock
);
6014 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
6017 con
->conwithperiod
);
6020 * No need to mark the constraint row as validated, we did
6021 * that when we inserted the row earlier.
6024 table_close(refrel
, NoLock
);
6029 table_close(rel
, NoLock
);
6032 /* Finally, run any afterStmts that were queued up */
6033 foreach(ltab
, *wqueue
)
6035 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
6038 foreach(lc
, tab
->afterStmts
)
6040 Node
*stmt
= (Node
*) lfirst(lc
);
6042 ProcessUtilityForAlterTable(stmt
, context
);
6043 CommandCounterIncrement();
6049 * ATRewriteTable: scan or rewrite one table
6051 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6052 * must already hold AccessExclusiveLock on it.
6055 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
)
6059 TupleDesc oldTupDesc
;
6060 TupleDesc newTupDesc
;
6061 bool needscan
= false;
6062 List
*notnull_attrs
;
6067 BulkInsertState bistate
;
6069 ExprState
*partqualstate
= NULL
;
6072 * Open the relation(s). We have surely already locked the existing
6075 oldrel
= table_open(tab
->relid
, NoLock
);
6076 oldTupDesc
= tab
->oldDesc
;
6077 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
6079 if (OidIsValid(OIDNewHeap
))
6081 Assert(CheckRelationOidLockedByMe(OIDNewHeap
, AccessExclusiveLock
,
6083 newrel
= table_open(OIDNewHeap
, NoLock
);
6089 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6090 * is empty, so don't bother using it.
6094 mycid
= GetCurrentCommandId(true);
6095 bistate
= GetBulkInsertState();
6096 ti_options
= TABLE_INSERT_SKIP_FSM
;
6100 /* keep compiler quiet about using these uninitialized */
6107 * Generate the constraint and default execution states
6110 estate
= CreateExecutorState();
6112 /* Build the needed expression execution states */
6113 foreach(l
, tab
->constraints
)
6115 NewConstraint
*con
= lfirst(l
);
6117 switch (con
->contype
)
6121 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
6123 case CONSTR_FOREIGN
:
6124 /* Nothing to do here */
6127 elog(ERROR
, "unrecognized constraint type: %d",
6128 (int) con
->contype
);
6132 /* Build expression execution states for partition check quals */
6133 if (tab
->partition_constraint
)
6136 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
6139 foreach(l
, tab
->newvals
)
6141 NewColumnValue
*ex
= lfirst(l
);
6143 /* expr already planned */
6144 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
6147 notnull_attrs
= NIL
;
6148 if (newrel
|| tab
->verify_new_notnull
)
6151 * If we are rebuilding the tuples OR if we added any new but not
6152 * verified not-null constraints, check all not-null constraints. This
6153 * is a bit of overkill but it minimizes risk of bugs.
6155 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6157 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
6159 if (attr
->attnotnull
&& !attr
->attisdropped
)
6160 notnull_attrs
= lappend_int(notnull_attrs
, i
);
6166 if (newrel
|| needscan
)
6168 ExprContext
*econtext
;
6169 TupleTableSlot
*oldslot
;
6170 TupleTableSlot
*newslot
;
6172 MemoryContext oldCxt
;
6173 List
*dropped_attrs
= NIL
;
6179 (errmsg_internal("rewriting table \"%s\"",
6180 RelationGetRelationName(oldrel
))));
6183 (errmsg_internal("verifying table \"%s\"",
6184 RelationGetRelationName(oldrel
))));
6189 * All predicate locks on the tuples or pages are about to be made
6190 * invalid, because we move tuples around. Promote them to
6193 TransferPredicateLocksToHeapRelation(oldrel
);
6196 econtext
= GetPerTupleExprContext(estate
);
6199 * Create necessary tuple slots. When rewriting, two slots are needed,
6200 * otherwise one suffices. In the case where one slot suffices, we
6201 * need to use the new tuple descriptor, otherwise some constraints
6202 * can't be evaluated. Note that even when the tuple layout is the
6203 * same and no rewrite is required, the tupDescs might not be
6204 * (consider ADD COLUMN without a default).
6208 Assert(newrel
!= NULL
);
6209 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
6210 table_slot_callbacks(oldrel
));
6211 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
6212 table_slot_callbacks(newrel
));
6215 * Set all columns in the new slot to NULL initially, to ensure
6216 * columns added as part of the rewrite are initialized to NULL.
6217 * That is necessary as tab->newvals will not contain an
6218 * expression for columns with a NULL default, e.g. when adding a
6219 * column without a default together with a column with a default
6220 * requiring an actual rewrite.
6222 ExecStoreAllNullTuple(newslot
);
6226 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
6227 table_slot_callbacks(oldrel
));
6232 * Any attributes that are dropped according to the new tuple
6233 * descriptor can be set to NULL. We precompute the list of dropped
6234 * attributes to avoid needing to do so in the per-tuple loop.
6236 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6238 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
6239 dropped_attrs
= lappend_int(dropped_attrs
, i
);
6243 * Scan through the rows, generating a new row if needed and then
6244 * checking all the constraints.
6246 snapshot
= RegisterSnapshot(GetLatestSnapshot());
6247 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
6250 * Switch to per-tuple memory context and reset it for each tuple
6251 * produced, so we don't leak memory.
6253 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
6255 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
6257 TupleTableSlot
*insertslot
;
6259 if (tab
->rewrite
> 0)
6261 /* Extract data from old tuple */
6262 slot_getallattrs(oldslot
);
6263 ExecClearTuple(newslot
);
6265 /* copy attributes */
6266 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
6267 sizeof(Datum
) * oldslot
->tts_nvalid
);
6268 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
6269 sizeof(bool) * oldslot
->tts_nvalid
);
6271 /* Set dropped attributes to null in new tuple */
6272 foreach(lc
, dropped_attrs
)
6273 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
6276 * Constraints and GENERATED expressions might reference the
6277 * tableoid column, so fill tts_tableOid with the desired
6278 * value. (We must do this each time, because it gets
6279 * overwritten with newrel's OID during storing.)
6281 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
6284 * Process supplied expressions to replace selected columns.
6286 * First, evaluate expressions whose inputs come from the old
6289 econtext
->ecxt_scantuple
= oldslot
;
6291 foreach(l
, tab
->newvals
)
6293 NewColumnValue
*ex
= lfirst(l
);
6295 if (ex
->is_generated
)
6298 newslot
->tts_values
[ex
->attnum
- 1]
6299 = ExecEvalExpr(ex
->exprstate
,
6301 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6304 ExecStoreVirtualTuple(newslot
);
6307 * Now, evaluate any expressions whose inputs come from the
6308 * new tuple. We assume these columns won't reference each
6309 * other, so that there's no ordering dependency.
6311 econtext
->ecxt_scantuple
= newslot
;
6313 foreach(l
, tab
->newvals
)
6315 NewColumnValue
*ex
= lfirst(l
);
6317 if (!ex
->is_generated
)
6320 newslot
->tts_values
[ex
->attnum
- 1]
6321 = ExecEvalExpr(ex
->exprstate
,
6323 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6326 insertslot
= newslot
;
6331 * If there's no rewrite, old and new table are guaranteed to
6332 * have the same AM, so we can just use the old slot to verify
6333 * new constraints etc.
6335 insertslot
= oldslot
;
6338 /* Now check any constraints on the possibly-changed tuple */
6339 econtext
->ecxt_scantuple
= insertslot
;
6341 foreach(l
, notnull_attrs
)
6343 int attn
= lfirst_int(l
);
6345 if (slot_attisnull(insertslot
, attn
+ 1))
6347 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6350 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6351 errmsg("column \"%s\" of relation \"%s\" contains null values",
6352 NameStr(attr
->attname
),
6353 RelationGetRelationName(oldrel
)),
6354 errtablecol(oldrel
, attn
+ 1)));
6358 foreach(l
, tab
->constraints
)
6360 NewConstraint
*con
= lfirst(l
);
6362 switch (con
->contype
)
6365 if (!ExecCheck(con
->qualstate
, econtext
))
6367 (errcode(ERRCODE_CHECK_VIOLATION
),
6368 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6370 RelationGetRelationName(oldrel
)),
6371 errtableconstraint(oldrel
, con
->name
)));
6373 case CONSTR_NOTNULL
:
6374 case CONSTR_FOREIGN
:
6375 /* Nothing to do here */
6378 elog(ERROR
, "unrecognized constraint type: %d",
6379 (int) con
->contype
);
6383 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6385 if (tab
->validate_default
)
6387 (errcode(ERRCODE_CHECK_VIOLATION
),
6388 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6389 RelationGetRelationName(oldrel
)),
6393 (errcode(ERRCODE_CHECK_VIOLATION
),
6394 errmsg("partition constraint of relation \"%s\" is violated by some row",
6395 RelationGetRelationName(oldrel
)),
6399 /* Write the tuple out to the new relation */
6401 table_tuple_insert(newrel
, insertslot
, mycid
,
6402 ti_options
, bistate
);
6404 ResetExprContext(econtext
);
6406 CHECK_FOR_INTERRUPTS();
6409 MemoryContextSwitchTo(oldCxt
);
6410 table_endscan(scan
);
6411 UnregisterSnapshot(snapshot
);
6413 ExecDropSingleTupleTableSlot(oldslot
);
6415 ExecDropSingleTupleTableSlot(newslot
);
6418 FreeExecutorState(estate
);
6420 table_close(oldrel
, NoLock
);
6423 FreeBulkInsertState(bistate
);
6425 table_finish_bulk_insert(newrel
, ti_options
);
6427 table_close(newrel
, NoLock
);
6432 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6434 static AlteredTableInfo
*
6435 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6437 Oid relid
= RelationGetRelid(rel
);
6438 AlteredTableInfo
*tab
;
6441 foreach(ltab
, *wqueue
)
6443 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6444 if (tab
->relid
== relid
)
6449 * Not there, so add it. Note that we make a copy of the relation's
6450 * existing descriptor before anything interesting can happen to it.
6452 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6454 tab
->rel
= NULL
; /* set later */
6455 tab
->relkind
= rel
->rd_rel
->relkind
;
6456 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6457 tab
->newAccessMethod
= InvalidOid
;
6458 tab
->chgAccessMethod
= false;
6459 tab
->newTableSpace
= InvalidOid
;
6460 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6461 tab
->chgPersistence
= false;
6463 *wqueue
= lappend(*wqueue
, tab
);
6469 alter_table_type_to_string(AlterTableType cmdtype
)
6474 case AT_AddColumnToView
:
6475 return "ADD COLUMN";
6476 case AT_ColumnDefault
:
6477 case AT_CookedColumnDefault
:
6478 return "ALTER COLUMN ... SET DEFAULT";
6479 case AT_DropNotNull
:
6480 return "ALTER COLUMN ... DROP NOT NULL";
6482 return "ALTER COLUMN ... SET NOT NULL";
6483 case AT_SetExpression
:
6484 return "ALTER COLUMN ... SET EXPRESSION";
6485 case AT_DropExpression
:
6486 return "ALTER COLUMN ... DROP EXPRESSION";
6487 case AT_SetStatistics
:
6488 return "ALTER COLUMN ... SET STATISTICS";
6490 return "ALTER COLUMN ... SET";
6491 case AT_ResetOptions
:
6492 return "ALTER COLUMN ... RESET";
6494 return "ALTER COLUMN ... SET STORAGE";
6495 case AT_SetCompression
:
6496 return "ALTER COLUMN ... SET COMPRESSION";
6498 return "DROP COLUMN";
6501 return NULL
; /* not real grammar */
6502 case AT_AddConstraint
:
6503 case AT_ReAddConstraint
:
6504 case AT_ReAddDomainConstraint
:
6505 case AT_AddIndexConstraint
:
6506 return "ADD CONSTRAINT";
6507 case AT_AlterConstraint
:
6508 return "ALTER CONSTRAINT";
6509 case AT_ValidateConstraint
:
6510 return "VALIDATE CONSTRAINT";
6511 case AT_DropConstraint
:
6512 return "DROP CONSTRAINT";
6513 case AT_ReAddComment
:
6514 return NULL
; /* not real grammar */
6515 case AT_AlterColumnType
:
6516 return "ALTER COLUMN ... SET DATA TYPE";
6517 case AT_AlterColumnGenericOptions
:
6518 return "ALTER COLUMN ... OPTIONS";
6519 case AT_ChangeOwner
:
6522 return "CLUSTER ON";
6523 case AT_DropCluster
:
6524 return "SET WITHOUT CLUSTER";
6525 case AT_SetAccessMethod
:
6526 return "SET ACCESS METHOD";
6528 return "SET LOGGED";
6529 case AT_SetUnLogged
:
6530 return "SET UNLOGGED";
6532 return "SET WITHOUT OIDS";
6533 case AT_SetTableSpace
:
6534 return "SET TABLESPACE";
6535 case AT_SetRelOptions
:
6537 case AT_ResetRelOptions
:
6539 case AT_ReplaceRelOptions
:
6540 return NULL
; /* not real grammar */
6542 return "ENABLE TRIGGER";
6543 case AT_EnableAlwaysTrig
:
6544 return "ENABLE ALWAYS TRIGGER";
6545 case AT_EnableReplicaTrig
:
6546 return "ENABLE REPLICA TRIGGER";
6547 case AT_DisableTrig
:
6548 return "DISABLE TRIGGER";
6549 case AT_EnableTrigAll
:
6550 return "ENABLE TRIGGER ALL";
6551 case AT_DisableTrigAll
:
6552 return "DISABLE TRIGGER ALL";
6553 case AT_EnableTrigUser
:
6554 return "ENABLE TRIGGER USER";
6555 case AT_DisableTrigUser
:
6556 return "DISABLE TRIGGER USER";
6558 return "ENABLE RULE";
6559 case AT_EnableAlwaysRule
:
6560 return "ENABLE ALWAYS RULE";
6561 case AT_EnableReplicaRule
:
6562 return "ENABLE REPLICA RULE";
6563 case AT_DisableRule
:
6564 return "DISABLE RULE";
6567 case AT_DropInherit
:
6568 return "NO INHERIT";
6573 case AT_ReplicaIdentity
:
6574 return "REPLICA IDENTITY";
6575 case AT_EnableRowSecurity
:
6576 return "ENABLE ROW SECURITY";
6577 case AT_DisableRowSecurity
:
6578 return "DISABLE ROW SECURITY";
6579 case AT_ForceRowSecurity
:
6580 return "FORCE ROW SECURITY";
6581 case AT_NoForceRowSecurity
:
6582 return "NO FORCE ROW SECURITY";
6583 case AT_GenericOptions
:
6585 case AT_AttachPartition
:
6586 return "ATTACH PARTITION";
6587 case AT_DetachPartition
:
6588 return "DETACH PARTITION";
6589 case AT_DetachPartitionFinalize
:
6590 return "DETACH PARTITION ... FINALIZE";
6591 case AT_AddIdentity
:
6592 return "ALTER COLUMN ... ADD IDENTITY";
6593 case AT_SetIdentity
:
6594 return "ALTER COLUMN ... SET";
6595 case AT_DropIdentity
:
6596 return "ALTER COLUMN ... DROP IDENTITY";
6597 case AT_ReAddStatistics
:
6598 return NULL
; /* not real grammar */
6605 * ATSimplePermissions
6607 * - Ensure that it is a relation (or possibly a view)
6608 * - Ensure this user is the owner
6609 * - Ensure that it is not a system table
6612 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6616 switch (rel
->rd_rel
->relkind
)
6618 case RELKIND_RELATION
:
6619 actual_target
= ATT_TABLE
;
6621 case RELKIND_PARTITIONED_TABLE
:
6622 actual_target
= ATT_PARTITIONED_TABLE
;
6625 actual_target
= ATT_VIEW
;
6627 case RELKIND_MATVIEW
:
6628 actual_target
= ATT_MATVIEW
;
6631 actual_target
= ATT_INDEX
;
6633 case RELKIND_PARTITIONED_INDEX
:
6634 actual_target
= ATT_PARTITIONED_INDEX
;
6636 case RELKIND_COMPOSITE_TYPE
:
6637 actual_target
= ATT_COMPOSITE_TYPE
;
6639 case RELKIND_FOREIGN_TABLE
:
6640 actual_target
= ATT_FOREIGN_TABLE
;
6642 case RELKIND_SEQUENCE
:
6643 actual_target
= ATT_SEQUENCE
;
6650 /* Wrong target type? */
6651 if ((actual_target
& allowed_targets
) == 0)
6653 const char *action_str
= alter_table_type_to_string(cmdtype
);
6657 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6658 /* translator: %s is a group of some SQL keywords */
6659 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6660 action_str
, RelationGetRelationName(rel
)),
6661 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6663 /* internal error? */
6664 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6665 RelationGetRelationName(rel
));
6668 /* Permissions checks */
6669 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6670 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6671 RelationGetRelationName(rel
));
6673 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6675 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6676 errmsg("permission denied: \"%s\" is a system catalog",
6677 RelationGetRelationName(rel
))));
6683 * Simple table recursion sufficient for most ALTER TABLE operations.
6684 * All direct and indirect children are processed in an unspecified order.
6685 * Note that if a child inherits from the original table via multiple
6686 * inheritance paths, it will be visited just once.
6689 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6690 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6691 AlterTableUtilityContext
*context
)
6694 * Propagate to children, if desired and if there are (or might be) any
6697 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6699 Oid relid
= RelationGetRelid(rel
);
6703 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6706 * find_all_inheritors does the recursive search of the inheritance
6707 * hierarchy, so all we have to do is process all of the relids in the
6708 * list that it returns.
6710 foreach(child
, children
)
6712 Oid childrelid
= lfirst_oid(child
);
6715 if (childrelid
== relid
)
6717 /* find_all_inheritors already got lock */
6718 childrel
= relation_open(childrelid
, NoLock
);
6719 CheckAlterTableIsSafe(childrel
);
6720 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6721 relation_close(childrel
, NoLock
);
6727 * Obtain list of partitions of the given table, locking them all at the given
6728 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6730 * This function is a no-op if the given relation is not a partitioned table;
6731 * in particular, nothing is done if it's a legacy inheritance parent.
6734 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6736 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6741 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6742 /* first element is the parent rel; must ignore it */
6743 for_each_from(cell
, inh
, 1)
6747 /* find_all_inheritors already got lock */
6748 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6749 CheckAlterTableIsSafe(childrel
);
6750 table_close(childrel
, NoLock
);
6757 * ATTypedTableRecursion
6759 * Propagate ALTER TYPE operations to the typed tables of that type.
6760 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6761 * recursion to inheritance children of the typed tables.
6764 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6765 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6770 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6772 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6773 RelationGetRelationName(rel
),
6776 foreach(child
, children
)
6778 Oid childrelid
= lfirst_oid(child
);
6781 childrel
= relation_open(childrelid
, lockmode
);
6782 CheckAlterTableIsSafe(childrel
);
6783 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6784 relation_close(childrel
, NoLock
);
6790 * find_composite_type_dependencies
6792 * Check to see if the type "typeOid" is being used as a column in some table
6793 * (possibly nested several levels deep in composite types, arrays, etc!).
6794 * Eventually, we'd like to propagate the check or rewrite operation
6795 * into such tables, but for now, just error out if we find any.
6797 * Caller should provide either the associated relation of a rowtype,
6798 * or a type name (not both) for use in the error message, if any.
6800 * Note that "typeOid" is not necessarily a composite type; it could also be
6801 * another container type such as an array or range, or a domain over one of
6802 * these things. The name of this function is therefore somewhat historical,
6803 * but it's not worth changing.
6805 * We assume that functions and views depending on the type are not reasons
6806 * to reject the ALTER. (How safe is this really?)
6809 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6810 const char *origTypeName
)
6814 SysScanDesc depScan
;
6817 /* since this function recurses, it could be driven to stack overflow */
6818 check_stack_depth();
6821 * We scan pg_depend to find those things that depend on the given type.
6822 * (We assume we can ignore refobjsubid for a type.)
6824 depRel
= table_open(DependRelationId
, AccessShareLock
);
6826 ScanKeyInit(&key
[0],
6827 Anum_pg_depend_refclassid
,
6828 BTEqualStrategyNumber
, F_OIDEQ
,
6829 ObjectIdGetDatum(TypeRelationId
));
6830 ScanKeyInit(&key
[1],
6831 Anum_pg_depend_refobjid
,
6832 BTEqualStrategyNumber
, F_OIDEQ
,
6833 ObjectIdGetDatum(typeOid
));
6835 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6838 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6840 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6842 TupleDesc tupleDesc
;
6843 Form_pg_attribute att
;
6845 /* Check for directly dependent types */
6846 if (pg_depend
->classid
== TypeRelationId
)
6849 * This must be an array, domain, or range containing the given
6850 * type, so recursively check for uses of this type. Note that
6851 * any error message will mention the original type not the
6852 * container; this is intentional.
6854 find_composite_type_dependencies(pg_depend
->objid
,
6855 origRelation
, origTypeName
);
6859 /* Else, ignore dependees that aren't relations */
6860 if (pg_depend
->classid
!= RelationRelationId
)
6863 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6864 tupleDesc
= RelationGetDescr(rel
);
6867 * If objsubid identifies a specific column, refer to that in error
6868 * messages. Otherwise, search to see if there's a user column of the
6869 * type. (We assume system columns are never of interesting types.)
6870 * The search is needed because an index containing an expression
6871 * column of the target type will just be recorded as a whole-relation
6872 * dependency. If we do not find a column of the type, the dependency
6873 * must indicate that the type is transiently referenced in an index
6874 * expression but not stored on disk, which we assume is OK, just as
6875 * we do for references in views. (It could also be that the target
6876 * type is embedded in some container type that is stored in an index
6877 * column, but the previous recursion should catch such cases.)
6879 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6880 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6884 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6886 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6887 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6893 /* No such column, so assume OK */
6894 relation_close(rel
, AccessShareLock
);
6900 * We definitely should reject if the relation has storage. If it's
6901 * partitioned, then perhaps we don't have to reject: if there are
6902 * partitions then we'll fail when we find one, else there is no
6903 * stored data to worry about. However, it's possible that the type
6904 * change would affect conclusions about whether the type is sortable
6905 * or hashable and thus (if it's a partitioning column) break the
6906 * partitioning rule. For now, reject for partitioned rels too.
6908 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6909 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6913 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6914 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6916 RelationGetRelationName(rel
),
6917 NameStr(att
->attname
))));
6918 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6920 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6921 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6922 RelationGetRelationName(origRelation
),
6923 RelationGetRelationName(rel
),
6924 NameStr(att
->attname
))));
6925 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6927 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6928 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6929 RelationGetRelationName(origRelation
),
6930 RelationGetRelationName(rel
),
6931 NameStr(att
->attname
))));
6934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6935 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6936 RelationGetRelationName(origRelation
),
6937 RelationGetRelationName(rel
),
6938 NameStr(att
->attname
))));
6940 else if (OidIsValid(rel
->rd_rel
->reltype
))
6943 * A view or composite type itself isn't a problem, but we must
6944 * recursively check for indirect dependencies via its rowtype.
6946 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6947 origRelation
, origTypeName
);
6950 relation_close(rel
, AccessShareLock
);
6953 systable_endscan(depScan
);
6955 relation_close(depRel
, AccessShareLock
);
6960 * find_typed_table_dependencies
6962 * Check to see if a composite type is being used as the type of a
6963 * typed table. Abort if any are found and behavior is RESTRICT.
6964 * Else return the list of tables.
6967 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6975 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6977 ScanKeyInit(&key
[0],
6978 Anum_pg_class_reloftype
,
6979 BTEqualStrategyNumber
, F_OIDEQ
,
6980 ObjectIdGetDatum(typeOid
));
6982 scan
= table_beginscan_catalog(classRel
, 1, key
);
6984 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6986 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6988 if (behavior
== DROP_RESTRICT
)
6990 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6991 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6993 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6995 result
= lappend_oid(result
, classform
->oid
);
6998 table_endscan(scan
);
6999 table_close(classRel
, AccessShareLock
);
7008 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7009 * isn't suitable, throw an error. Currently, we require that the type
7010 * originated with CREATE TYPE AS. We could support any row type, but doing so
7011 * would require handling a number of extra corner cases in the DDL commands.
7012 * (Also, allowing domain-over-composite would open up a can of worms about
7013 * whether and how the domain's constraints should apply to derived tables.)
7016 check_of_type(HeapTuple typetuple
)
7018 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
7019 bool typeOk
= false;
7021 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
7023 Relation typeRelation
;
7025 Assert(OidIsValid(typ
->typrelid
));
7026 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
7027 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
7030 * Close the parent rel, but keep our AccessShareLock on it until xact
7031 * commit. That will prevent someone else from deleting or ALTERing
7032 * the type before the typed table creation/conversion commits.
7034 relation_close(typeRelation
, NoLock
);
7038 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7039 errmsg("type %s is the row type of another table",
7040 format_type_be(typ
->oid
)),
7041 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7045 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7046 errmsg("type %s is not a composite type",
7047 format_type_be(typ
->oid
))));
7052 * ALTER TABLE ADD COLUMN
7054 * Adds an additional attribute to a relation making the assumption that
7055 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7056 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7059 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7060 * have to decide at runtime whether to recurse or not depending on whether we
7061 * actually add a column or merely merge with an existing column. (We can't
7062 * check this in a static pre-pass because it won't handle multiple inheritance
7063 * situations correctly.)
7066 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
7067 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
7068 AlterTableUtilityContext
*context
)
7070 if (rel
->rd_rel
->reloftype
&& !recursing
)
7072 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7073 errmsg("cannot add column to typed table")));
7075 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
7076 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
7078 if (recurse
&& !is_view
)
7079 cmd
->recurse
= true;
7083 * Add a column to a table. The return value is the address of the
7084 * new column in the parent relation.
7086 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7087 * copy (but that happens only after we check for IF NOT EXISTS).
7089 static ObjectAddress
7090 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
7091 AlterTableCmd
**cmd
, bool recurse
, bool recursing
,
7092 LOCKMODE lockmode
, AlterTablePass cur_pass
,
7093 AlterTableUtilityContext
*context
)
7095 Oid myrelid
= RelationGetRelid(rel
);
7096 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7097 bool if_not_exists
= (*cmd
)->missing_ok
;
7101 Form_pg_class relform
;
7102 Form_pg_attribute attribute
;
7108 AlterTableCmd
*childcmd
;
7109 ObjectAddress address
;
7112 /* since this function recurses, it could be driven to stack overflow */
7113 check_stack_depth();
7115 /* At top level, permission check was done in ATPrepCmd, else do it */
7117 ATSimplePermissions((*cmd
)->subtype
, rel
,
7118 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
7120 if (rel
->rd_rel
->relispartition
&& !recursing
)
7122 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7123 errmsg("cannot add column to a partition")));
7125 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
7128 * Are we adding the column to a recursion child? If so, check whether to
7129 * merge with an existing definition for the column. If we do merge, we
7130 * must not recurse. Children will already have the column, and recursing
7131 * into them would mess up attinhcount.
7133 if (colDef
->inhcount
> 0)
7137 /* Does child already have a column by this name? */
7138 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
7139 if (HeapTupleIsValid(tuple
))
7141 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7146 /* Child column must match on type, typmod, and collation */
7147 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
7148 if (ctypeId
!= childatt
->atttypid
||
7149 ctypmod
!= childatt
->atttypmod
)
7151 (errcode(ERRCODE_DATATYPE_MISMATCH
),
7152 errmsg("child table \"%s\" has different type for column \"%s\"",
7153 RelationGetRelationName(rel
), colDef
->colname
)));
7154 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
7155 if (ccollid
!= childatt
->attcollation
)
7157 (errcode(ERRCODE_COLLATION_MISMATCH
),
7158 errmsg("child table \"%s\" has different collation for column \"%s\"",
7159 RelationGetRelationName(rel
), colDef
->colname
),
7160 errdetail("\"%s\" versus \"%s\"",
7161 get_collation_name(ccollid
),
7162 get_collation_name(childatt
->attcollation
))));
7164 /* Bump the existing child att's inhcount */
7165 if (pg_add_s16_overflow(childatt
->attinhcount
, 1,
7166 &childatt
->attinhcount
))
7168 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7169 errmsg("too many inheritance parents"));
7170 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
7172 heap_freetuple(tuple
);
7174 /* Inform the user about the merge */
7176 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7177 colDef
->colname
, RelationGetRelationName(rel
))));
7179 table_close(attrdesc
, RowExclusiveLock
);
7181 /* Make the child column change visible */
7182 CommandCounterIncrement();
7184 return InvalidObjectAddress
;
7188 /* skip if the name already exists and if_not_exists is true */
7189 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
7191 table_close(attrdesc
, RowExclusiveLock
);
7192 return InvalidObjectAddress
;
7196 * Okay, we need to add the column, so go ahead and do parse
7197 * transformation. This can result in queueing up, or even immediately
7198 * executing, subsidiary operations (such as creation of unique indexes);
7199 * so we mustn't do it until we have made the if_not_exists check.
7201 * When recursing, the command was already transformed and we needn't do
7202 * so again. Also, if context isn't given we can't transform. (That
7203 * currently happens only for AT_AddColumnToView; we expect that view.c
7204 * passed us a ColumnDef that doesn't need work.)
7206 if (context
!= NULL
&& !recursing
)
7208 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
7210 Assert(*cmd
!= NULL
);
7211 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7215 * Regular inheritance children are independent enough not to inherit the
7216 * identity column from parent hence cannot recursively add identity
7217 * column if the table has inheritance children.
7219 * Partitions, on the other hand, are integral part of a partitioned table
7220 * and inherit identity column. Hence propagate identity column down the
7221 * partition hierarchy.
7223 if (colDef
->identity
&&
7225 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
&&
7226 find_inheritance_children(myrelid
, NoLock
) != NIL
)
7228 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7229 errmsg("cannot recursively add identity column to table that has child tables")));
7231 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
7233 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
7234 if (!HeapTupleIsValid(reltup
))
7235 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
7236 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
7237 relkind
= relform
->relkind
;
7239 /* Determine the new attribute's number */
7240 newattnum
= relform
->relnatts
+ 1;
7241 if (newattnum
> MaxHeapAttributeNumber
)
7243 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
7244 errmsg("tables can have at most %d columns",
7245 MaxHeapAttributeNumber
)));
7248 * Construct new attribute's pg_attribute entry.
7250 tupdesc
= BuildDescForRelation(list_make1(colDef
));
7252 attribute
= TupleDescAttr(tupdesc
, 0);
7254 /* Fix up attribute number */
7255 attribute
->attnum
= newattnum
;
7257 /* make sure datatype is legal for a column */
7258 CheckAttributeType(NameStr(attribute
->attname
), attribute
->atttypid
, attribute
->attcollation
,
7259 list_make1_oid(rel
->rd_rel
->reltype
),
7262 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
7264 table_close(attrdesc
, RowExclusiveLock
);
7267 * Update pg_class tuple as appropriate
7269 relform
->relnatts
= newattnum
;
7271 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
7273 heap_freetuple(reltup
);
7275 /* Post creation hook for new attribute */
7276 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
7278 table_close(pgclass
, RowExclusiveLock
);
7280 /* Make the attribute's catalog entry visible */
7281 CommandCounterIncrement();
7284 * Store the DEFAULT, if any, in the catalogs
7286 if (colDef
->raw_default
)
7288 RawColumnDefault
*rawEnt
;
7290 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7291 rawEnt
->attnum
= attribute
->attnum
;
7292 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7295 * Attempt to skip a complete table rewrite by storing the specified
7296 * DEFAULT value outside of the heap. This may be disabled inside
7297 * AddRelationNewConstraints if the optimization cannot be applied.
7299 rawEnt
->missingMode
= (!colDef
->generated
);
7301 rawEnt
->generated
= colDef
->generated
;
7304 * This function is intended for CREATE TABLE, so it processes a
7305 * _list_ of defaults, but we just do one.
7307 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7308 false, true, false, NULL
);
7310 /* Make the additional catalog changes visible */
7311 CommandCounterIncrement();
7314 * Did the request for a missing value work? If not we'll have to do a
7317 if (!rawEnt
->missingMode
)
7318 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7322 * Tell Phase 3 to fill in the default expression, if there is one.
7324 * If there is no default, Phase 3 doesn't have to do anything, because
7325 * that effectively means that the default is NULL. The heap tuple access
7326 * routines always check for attnum > # of attributes in tuple, and return
7327 * NULL if so, so without any modification of the tuple data we will get
7328 * the effect of NULL values in the new column.
7330 * An exception occurs when the new column is of a domain type: the domain
7331 * might have a not-null constraint, or a check constraint that indirectly
7332 * rejects nulls. If there are any domain constraints then we construct
7333 * an explicit NULL default value that will be passed through
7334 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7335 * rewriting the table which we really don't have to do, but the present
7336 * design of domain processing doesn't offer any simple way of checking
7337 * the constraints more directly.)
7339 * Note: we use build_column_default, and not just the cooked default
7340 * returned by AddRelationNewConstraints, so that the right thing happens
7341 * when a datatype's default applies.
7343 * Note: it might seem that this should happen at the end of Phase 2, so
7344 * that the effects of subsequent subcommands can be taken into account.
7345 * It's intentional that we do it now, though. The new column should be
7346 * filled according to what is said in the ADD COLUMN subcommand, so that
7347 * the effects are the same as if this subcommand had been run by itself
7348 * and the later subcommands had been issued in new ALTER TABLE commands.
7350 * We can skip this entirely for relations without storage, since Phase 3
7351 * is certainly not going to touch them. System attributes don't have
7352 * interesting defaults, either.
7354 if (RELKIND_HAS_STORAGE(relkind
))
7357 * For an identity column, we can't use build_column_default(),
7358 * because the sequence ownership isn't set yet. So do it manually.
7360 if (colDef
->identity
)
7362 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7364 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7365 nve
->typeId
= attribute
->atttypid
;
7367 defval
= (Expr
*) nve
;
7369 /* must do a rewrite for identity columns */
7370 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7373 defval
= (Expr
*) build_column_default(rel
, attribute
->attnum
);
7375 if (!defval
&& DomainHasConstraints(attribute
->atttypid
))
7381 baseTypeMod
= attribute
->atttypmod
;
7382 baseTypeId
= getBaseTypeAndTypmod(attribute
->atttypid
, &baseTypeMod
);
7383 baseTypeColl
= get_typcollation(baseTypeId
);
7384 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7385 defval
= (Expr
*) coerce_to_target_type(NULL
,
7388 attribute
->atttypid
,
7389 attribute
->atttypmod
,
7390 COERCION_ASSIGNMENT
,
7391 COERCE_IMPLICIT_CAST
,
7393 if (defval
== NULL
) /* should not happen */
7394 elog(ERROR
, "failed to coerce base type to domain");
7399 NewColumnValue
*newval
;
7401 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7402 newval
->attnum
= attribute
->attnum
;
7403 newval
->expr
= expression_planner(defval
);
7404 newval
->is_generated
= (colDef
->generated
!= '\0');
7406 tab
->newvals
= lappend(tab
->newvals
, newval
);
7409 if (DomainHasConstraints(attribute
->atttypid
))
7410 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7412 if (!TupleDescAttr(rel
->rd_att
, attribute
->attnum
- 1)->atthasmissing
)
7415 * If the new column is NOT NULL, and there is no missing value,
7416 * tell Phase 3 it needs to check for NULLs.
7418 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7423 * Add needed dependency entries for the new column.
7425 add_column_datatype_dependency(myrelid
, newattnum
, attribute
->atttypid
);
7426 add_column_collation_dependency(myrelid
, newattnum
, attribute
->attcollation
);
7429 * Propagate to children as appropriate. Unlike most other ALTER
7430 * routines, we have to do this one level of recursion at a time; we can't
7431 * use find_all_inheritors to do it in one pass.
7434 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7437 * If we are told not to recurse, there had better not be any child
7438 * tables; else the addition would put them out of step.
7440 if (children
&& !recurse
)
7442 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7443 errmsg("column must be added to child tables too")));
7445 /* Children should see column as singly inherited */
7448 childcmd
= copyObject(*cmd
);
7449 colDef
= castNode(ColumnDef
, childcmd
->def
);
7450 colDef
->inhcount
= 1;
7451 colDef
->is_local
= false;
7454 childcmd
= *cmd
; /* no need to copy again */
7456 foreach(child
, children
)
7458 Oid childrelid
= lfirst_oid(child
);
7460 AlteredTableInfo
*childtab
;
7462 /* find_inheritance_children already got lock */
7463 childrel
= table_open(childrelid
, NoLock
);
7464 CheckAlterTableIsSafe(childrel
);
7466 /* Find or create work queue entry for this table */
7467 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7469 /* Recurse to child; return value is ignored */
7470 ATExecAddColumn(wqueue
, childtab
, childrel
,
7471 &childcmd
, recurse
, true,
7472 lockmode
, cur_pass
, context
);
7474 table_close(childrel
, NoLock
);
7477 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7482 * If a new or renamed column will collide with the name of an existing
7483 * column and if_not_exists is false then error out, else do nothing.
7486 check_for_column_name_collision(Relation rel
, const char *colname
,
7493 * this test is deliberately not attisdropped-aware, since if one tries to
7494 * add a column matching a dropped column name, it's gonna fail anyway.
7496 attTuple
= SearchSysCache2(ATTNAME
,
7497 ObjectIdGetDatum(RelationGetRelid(rel
)),
7498 PointerGetDatum(colname
));
7499 if (!HeapTupleIsValid(attTuple
))
7502 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7503 ReleaseSysCache(attTuple
);
7506 * We throw a different error message for conflicts with system column
7507 * names, since they are normally not shown and the user might otherwise
7508 * be confused about the reason for the conflict.
7512 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7513 errmsg("column name \"%s\" conflicts with a system column name",
7520 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7521 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7522 colname
, RelationGetRelationName(rel
))));
7527 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7528 errmsg("column \"%s\" of relation \"%s\" already exists",
7529 colname
, RelationGetRelationName(rel
))));
7536 * Install a column's dependency on its datatype.
7539 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7541 ObjectAddress myself
,
7544 myself
.classId
= RelationRelationId
;
7545 myself
.objectId
= relid
;
7546 myself
.objectSubId
= attnum
;
7547 referenced
.classId
= TypeRelationId
;
7548 referenced
.objectId
= typid
;
7549 referenced
.objectSubId
= 0;
7550 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7554 * Install a column's dependency on its collation.
7557 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7559 ObjectAddress myself
,
7562 /* We know the default collation is pinned, so don't bother recording it */
7563 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7565 myself
.classId
= RelationRelationId
;
7566 myself
.objectId
= relid
;
7567 myself
.objectSubId
= attnum
;
7568 referenced
.classId
= CollationRelationId
;
7569 referenced
.objectId
= collid
;
7570 referenced
.objectSubId
= 0;
7571 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7576 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7578 * Return the address of the modified column. If the column was already
7579 * nullable, InvalidObjectAddress is returned.
7581 static ObjectAddress
7582 ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
7587 Form_pg_attribute attTup
;
7590 ObjectAddress address
;
7593 * lookup the attribute
7595 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7597 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7598 if (!HeapTupleIsValid(tuple
))
7600 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7601 errmsg("column \"%s\" of relation \"%s\" does not exist",
7602 colName
, RelationGetRelationName(rel
))));
7603 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7604 attnum
= attTup
->attnum
;
7605 ObjectAddressSubSet(address
, RelationRelationId
,
7606 RelationGetRelid(rel
), attnum
);
7608 /* If the column is already nullable there's nothing to do. */
7609 if (!attTup
->attnotnull
)
7611 table_close(attr_rel
, RowExclusiveLock
);
7612 return InvalidObjectAddress
;
7615 /* Prevent them from altering a system attribute */
7618 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7619 errmsg("cannot alter system column \"%s\"",
7622 if (attTup
->attidentity
)
7624 (errcode(ERRCODE_SYNTAX_ERROR
),
7625 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7626 colName
, RelationGetRelationName(rel
))));
7629 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7631 if (rel
->rd_rel
->relispartition
)
7633 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7634 Relation parent
= table_open(parentId
, AccessShareLock
);
7635 TupleDesc tupDesc
= RelationGetDescr(parent
);
7636 AttrNumber parent_attnum
;
7638 parent_attnum
= get_attnum(parentId
, colName
);
7639 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7641 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7642 errmsg("column \"%s\" is marked NOT NULL in parent table",
7644 table_close(parent
, AccessShareLock
);
7648 * Find the constraint that makes this column NOT NULL, and drop it.
7649 * dropconstraint_internal() resets attnotnull.
7651 conTup
= findNotNullConstraintAttnum(RelationGetRelid(rel
), attnum
);
7653 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7654 colName
, RelationGetRelationName(rel
));
7656 /* The normal case: we have a pg_constraint row, remove it */
7657 dropconstraint_internal(rel
, conTup
, DROP_RESTRICT
, recurse
, false,
7659 heap_freetuple(conTup
);
7661 InvokeObjectPostAlterHook(RelationRelationId
,
7662 RelationGetRelid(rel
), attnum
);
7664 table_close(attr_rel
, RowExclusiveLock
);
7670 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7673 * When called to alter an existing table, 'wqueue' must be given so that we
7674 * can queue a check that existing tuples pass the constraint. When called
7675 * from table creation, 'wqueue' should be passed as NULL.
7678 set_attnotnull(List
**wqueue
, Relation rel
, AttrNumber attnum
,
7681 Form_pg_attribute attr
;
7683 CheckAlterTableIsSafe(rel
);
7686 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7689 attr
= TupleDescAttr(RelationGetDescr(rel
), attnum
- 1);
7690 if (attr
->attisdropped
)
7693 if (!attr
->attnotnull
)
7698 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7700 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
7701 if (!HeapTupleIsValid(tuple
))
7702 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
7703 attnum
, RelationGetRelid(rel
));
7705 attr
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7706 Assert(!attr
->attnotnull
);
7707 attr
->attnotnull
= true;
7708 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7711 * If the nullness isn't already proven by validated constraints, have
7712 * ALTER TABLE phase 3 test for it.
7714 if (wqueue
&& !NotNullImpliedByRelConstraints(rel
, attr
))
7716 AlteredTableInfo
*tab
;
7718 tab
= ATGetQueueEntry(wqueue
, rel
);
7719 tab
->verify_new_notnull
= true;
7722 CommandCounterIncrement();
7724 table_close(attr_rel
, RowExclusiveLock
);
7725 heap_freetuple(tuple
);
7730 * ALTER TABLE ALTER COLUMN SET NOT NULL
7732 * Add a not-null constraint to a single table and its children. Returns
7733 * the address of the constraint added to the parent relation, if one gets
7734 * added, or InvalidObjectAddress otherwise.
7736 * We must recurse to child tables during execution, rather than using
7737 * ALTER TABLE's normal prep-time recursion.
7739 static ObjectAddress
7740 ATExecSetNotNull(List
**wqueue
, Relation rel
, char *conName
, char *colName
,
7741 bool recurse
, bool recursing
, LOCKMODE lockmode
)
7745 ObjectAddress address
;
7746 Constraint
*constraint
;
7747 CookedConstraint
*ccon
;
7749 bool is_no_inherit
= false;
7751 /* Guard against stack overflow due to overly deep inheritance tree. */
7752 check_stack_depth();
7754 /* At top level, permission check was done in ATPrepCmd, else do it */
7757 ATSimplePermissions(AT_AddConstraint
, rel
,
7758 ATT_PARTITIONED_TABLE
| ATT_TABLE
| ATT_FOREIGN_TABLE
);
7759 Assert(conName
!= NULL
);
7762 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7763 if (attnum
== InvalidAttrNumber
)
7765 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7766 errmsg("column \"%s\" of relation \"%s\" does not exist",
7767 colName
, RelationGetRelationName(rel
))));
7769 /* Prevent them from altering a system attribute */
7772 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7773 errmsg("cannot alter system column \"%s\"",
7776 /* See if there's already a constraint */
7777 tuple
= findNotNullConstraintAttnum(RelationGetRelid(rel
), attnum
);
7778 if (HeapTupleIsValid(tuple
))
7780 Form_pg_constraint conForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
7781 bool changed
= false;
7784 * Don't let a NO INHERIT constraint be changed into inherit.
7786 if (conForm
->connoinherit
&& recurse
)
7788 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7789 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7790 NameStr(conForm
->conname
),
7791 RelationGetRelationName(rel
)));
7794 * If we find an appropriate constraint, we're almost done, but just
7795 * need to change some properties on it: if we're recursing, increment
7796 * coninhcount; if not, set conislocal if not already set.
7800 if (pg_add_s16_overflow(conForm
->coninhcount
, 1,
7801 &conForm
->coninhcount
))
7803 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7804 errmsg("too many inheritance parents"));
7807 else if (!conForm
->conislocal
)
7809 conForm
->conislocal
= true;
7815 Relation constr_rel
;
7817 constr_rel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
7819 CatalogTupleUpdate(constr_rel
, &tuple
->t_self
, tuple
);
7820 ObjectAddressSet(address
, ConstraintRelationId
, conForm
->oid
);
7821 table_close(constr_rel
, RowExclusiveLock
);
7827 return InvalidObjectAddress
;
7831 * If we're asked not to recurse, and children exist, raise an error for
7832 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7836 find_inheritance_children(RelationGetRelid(rel
),
7839 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7841 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7842 errmsg("constraint must be added to child tables too"),
7843 errhint("Do not specify the ONLY keyword."));
7845 is_no_inherit
= true;
7849 * No constraint exists; we must add one. First determine a name to use,
7850 * if we haven't already.
7854 Assert(conName
== NULL
);
7855 conName
= ChooseConstraintName(RelationGetRelationName(rel
),
7856 colName
, "not_null",
7857 RelationGetNamespace(rel
),
7861 constraint
= makeNotNullConstraint(makeString(colName
));
7862 constraint
->is_no_inherit
= is_no_inherit
;
7863 constraint
->conname
= conName
;
7866 cooked
= AddRelationNewConstraints(rel
, NIL
, list_make1(constraint
),
7867 false, !recursing
, false, NULL
);
7868 ccon
= linitial(cooked
);
7869 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
7871 InvokeObjectPostAlterHook(RelationRelationId
,
7872 RelationGetRelid(rel
), attnum
);
7874 /* Mark pg_attribute.attnotnull for the column */
7875 set_attnotnull(wqueue
, rel
, attnum
, lockmode
);
7878 * Recurse to propagate the constraint to children that don't have one.
7884 children
= find_inheritance_children(RelationGetRelid(rel
),
7887 foreach_oid(childoid
, children
)
7889 Relation childrel
= table_open(childoid
, NoLock
);
7891 CommandCounterIncrement();
7893 ATExecSetNotNull(wqueue
, childrel
, conName
, colName
,
7894 recurse
, true, lockmode
);
7895 table_close(childrel
, NoLock
);
7903 * NotNullImpliedByRelConstraints
7904 * Does rel's existing constraints imply NOT NULL for the given attribute?
7907 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
7909 NullTest
*nnulltest
= makeNode(NullTest
);
7911 nnulltest
->arg
= (Expr
*) makeVar(1,
7917 nnulltest
->nulltesttype
= IS_NOT_NULL
;
7920 * argisrow = false is correct even for a composite column, because
7921 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7922 * case, just IS DISTINCT FROM NULL.
7924 nnulltest
->argisrow
= false;
7925 nnulltest
->location
= -1;
7927 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
7930 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7931 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
7939 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7941 * Return the address of the affected column.
7943 static ObjectAddress
7944 ATExecColumnDefault(Relation rel
, const char *colName
,
7945 Node
*newDefault
, LOCKMODE lockmode
)
7947 TupleDesc tupdesc
= RelationGetDescr(rel
);
7949 ObjectAddress address
;
7952 * get the number of the attribute
7954 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7955 if (attnum
== InvalidAttrNumber
)
7957 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7958 errmsg("column \"%s\" of relation \"%s\" does not exist",
7959 colName
, RelationGetRelationName(rel
))));
7961 /* Prevent them from altering a system attribute */
7964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7965 errmsg("cannot alter system column \"%s\"",
7968 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
7970 (errcode(ERRCODE_SYNTAX_ERROR
),
7971 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7972 colName
, RelationGetRelationName(rel
)),
7973 /* translator: %s is an SQL ALTER command */
7974 newDefault
? 0 : errhint("Use %s instead.",
7975 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7977 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
7979 (errcode(ERRCODE_SYNTAX_ERROR
),
7980 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7981 colName
, RelationGetRelationName(rel
)),
7983 /* translator: %s is an SQL ALTER command */
7984 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7985 (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
== ATTRIBUTE_GENERATED_STORED
?
7986 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7989 * Remove any old default for the column. We use RESTRICT here for
7990 * safety, but at present we do not expect anything to depend on the
7993 * We treat removing the existing default as an internal operation when it
7994 * is preparatory to adding a new default, but as a user-initiated
7995 * operation when the user asked for a drop.
7997 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
7998 newDefault
!= NULL
);
8003 RawColumnDefault
*rawEnt
;
8005 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8006 rawEnt
->attnum
= attnum
;
8007 rawEnt
->raw_default
= newDefault
;
8008 rawEnt
->missingMode
= false;
8009 rawEnt
->generated
= '\0';
8012 * This function is intended for CREATE TABLE, so it processes a
8013 * _list_ of defaults, but we just do one.
8015 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8016 false, true, false, NULL
);
8019 ObjectAddressSubSet(address
, RelationRelationId
,
8020 RelationGetRelid(rel
), attnum
);
8025 * Add a pre-cooked default expression.
8027 * Return the address of the affected column.
8029 static ObjectAddress
8030 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
8033 ObjectAddress address
;
8035 /* We assume no checking is required */
8038 * Remove any old default for the column. We use RESTRICT here for
8039 * safety, but at present we do not expect anything to depend on the
8040 * default. (In ordinary cases, there could not be a default in place
8041 * anyway, but it's possible when combining LIKE with inheritance.)
8043 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8046 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
8048 ObjectAddressSubSet(address
, RelationRelationId
,
8049 RelationGetRelid(rel
), attnum
);
8054 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8056 * Return the address of the affected column.
8058 static ObjectAddress
8059 ATExecAddIdentity(Relation rel
, const char *colName
,
8060 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
)
8062 Relation attrelation
;
8064 Form_pg_attribute attTup
;
8066 ObjectAddress address
;
8067 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
8070 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8071 if (ispartitioned
&& !recurse
)
8073 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8074 errmsg("cannot add identity to a column of only the partitioned table"),
8075 errhint("Do not specify the ONLY keyword.")));
8077 if (rel
->rd_rel
->relispartition
&& !recursing
)
8079 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8080 errmsg("cannot add identity to a column of a partition"));
8082 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8084 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8085 if (!HeapTupleIsValid(tuple
))
8087 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8088 errmsg("column \"%s\" of relation \"%s\" does not exist",
8089 colName
, RelationGetRelationName(rel
))));
8090 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8091 attnum
= attTup
->attnum
;
8093 /* Can't alter a system attribute */
8096 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8097 errmsg("cannot alter system column \"%s\"",
8101 * Creating a column as identity implies NOT NULL, so adding the identity
8102 * to an existing column that is not NOT NULL would create a state that
8103 * cannot be reproduced without contortions.
8105 if (!attTup
->attnotnull
)
8107 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8108 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8109 colName
, RelationGetRelationName(rel
))));
8111 if (attTup
->attidentity
)
8113 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8114 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8115 colName
, RelationGetRelationName(rel
))));
8117 if (attTup
->atthasdef
)
8119 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8120 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8121 colName
, RelationGetRelationName(rel
))));
8123 attTup
->attidentity
= cdef
->identity
;
8124 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8126 InvokeObjectPostAlterHook(RelationRelationId
,
8127 RelationGetRelid(rel
),
8129 ObjectAddressSubSet(address
, RelationRelationId
,
8130 RelationGetRelid(rel
), attnum
);
8131 heap_freetuple(tuple
);
8133 table_close(attrelation
, RowExclusiveLock
);
8136 * Recurse to propagate the identity column to partitions. Identity is
8137 * not inherited in regular inheritance children.
8139 if (recurse
&& ispartitioned
)
8144 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8146 foreach(lc
, children
)
8150 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8151 ATExecAddIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8152 table_close(childrel
, NoLock
);
8160 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8162 * Return the address of the affected column.
8164 static ObjectAddress
8165 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
,
8166 LOCKMODE lockmode
, bool recurse
, bool recursing
)
8169 DefElem
*generatedEl
= NULL
;
8171 Form_pg_attribute attTup
;
8173 Relation attrelation
;
8174 ObjectAddress address
;
8177 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8178 if (ispartitioned
&& !recurse
)
8180 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8181 errmsg("cannot change identity column of only the partitioned table"),
8182 errhint("Do not specify the ONLY keyword.")));
8184 if (rel
->rd_rel
->relispartition
&& !recursing
)
8186 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8187 errmsg("cannot change identity column of a partition"));
8189 foreach(option
, castNode(List
, def
))
8191 DefElem
*defel
= lfirst_node(DefElem
, option
);
8193 if (strcmp(defel
->defname
, "generated") == 0)
8197 (errcode(ERRCODE_SYNTAX_ERROR
),
8198 errmsg("conflicting or redundant options")));
8199 generatedEl
= defel
;
8202 elog(ERROR
, "option \"%s\" not recognized",
8207 * Even if there is nothing to change here, we run all the checks. There
8208 * will be a subsequent ALTER SEQUENCE that relies on everything being
8212 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8213 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8214 if (!HeapTupleIsValid(tuple
))
8216 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8217 errmsg("column \"%s\" of relation \"%s\" does not exist",
8218 colName
, RelationGetRelationName(rel
))));
8220 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8221 attnum
= attTup
->attnum
;
8225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8226 errmsg("cannot alter system column \"%s\"",
8229 if (!attTup
->attidentity
)
8231 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8232 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8233 colName
, RelationGetRelationName(rel
))));
8237 attTup
->attidentity
= defGetInt32(generatedEl
);
8238 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8240 InvokeObjectPostAlterHook(RelationRelationId
,
8241 RelationGetRelid(rel
),
8243 ObjectAddressSubSet(address
, RelationRelationId
,
8244 RelationGetRelid(rel
), attnum
);
8247 address
= InvalidObjectAddress
;
8249 heap_freetuple(tuple
);
8250 table_close(attrelation
, RowExclusiveLock
);
8253 * Recurse to propagate the identity change to partitions. Identity is not
8254 * inherited in regular inheritance children.
8256 if (generatedEl
&& recurse
&& ispartitioned
)
8261 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8263 foreach(lc
, children
)
8267 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8268 ATExecSetIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8269 table_close(childrel
, NoLock
);
8277 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8279 * Return the address of the affected column.
8281 static ObjectAddress
8282 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
8283 bool recurse
, bool recursing
)
8286 Form_pg_attribute attTup
;
8288 Relation attrelation
;
8289 ObjectAddress address
;
8291 ObjectAddress seqaddress
;
8294 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8295 if (ispartitioned
&& !recurse
)
8297 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8298 errmsg("cannot drop identity from a column of only the partitioned table"),
8299 errhint("Do not specify the ONLY keyword.")));
8301 if (rel
->rd_rel
->relispartition
&& !recursing
)
8303 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8304 errmsg("cannot drop identity from a column of a partition"));
8306 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8307 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8308 if (!HeapTupleIsValid(tuple
))
8310 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8311 errmsg("column \"%s\" of relation \"%s\" does not exist",
8312 colName
, RelationGetRelationName(rel
))));
8314 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8315 attnum
= attTup
->attnum
;
8319 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8320 errmsg("cannot alter system column \"%s\"",
8323 if (!attTup
->attidentity
)
8327 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8328 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8329 colName
, RelationGetRelationName(rel
))));
8333 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8334 colName
, RelationGetRelationName(rel
))));
8335 heap_freetuple(tuple
);
8336 table_close(attrelation
, RowExclusiveLock
);
8337 return InvalidObjectAddress
;
8341 attTup
->attidentity
= '\0';
8342 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8344 InvokeObjectPostAlterHook(RelationRelationId
,
8345 RelationGetRelid(rel
),
8347 ObjectAddressSubSet(address
, RelationRelationId
,
8348 RelationGetRelid(rel
), attnum
);
8349 heap_freetuple(tuple
);
8351 table_close(attrelation
, RowExclusiveLock
);
8354 * Recurse to drop the identity from column in partitions. Identity is
8355 * not inherited in regular inheritance children so ignore them.
8357 if (recurse
&& ispartitioned
)
8362 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8364 foreach(lc
, children
)
8368 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8369 ATExecDropIdentity(childrel
, colName
, false, lockmode
, recurse
, true);
8370 table_close(childrel
, NoLock
);
8376 /* drop the internal sequence */
8377 seqid
= getIdentitySequence(rel
, attnum
, false);
8378 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
8379 RelationRelationId
, DEPENDENCY_INTERNAL
);
8380 CommandCounterIncrement();
8381 seqaddress
.classId
= RelationRelationId
;
8382 seqaddress
.objectId
= seqid
;
8383 seqaddress
.objectSubId
= 0;
8384 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8391 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8393 * Return the address of the affected column.
8395 static ObjectAddress
8396 ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
8397 Node
*newExpr
, LOCKMODE lockmode
)
8400 Form_pg_attribute attTup
;
8403 ObjectAddress address
;
8405 NewColumnValue
*newval
;
8406 RawColumnDefault
*rawEnt
;
8408 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8409 if (!HeapTupleIsValid(tuple
))
8411 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8412 errmsg("column \"%s\" of relation \"%s\" does not exist",
8413 colName
, RelationGetRelationName(rel
))));
8415 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8416 attnum
= attTup
->attnum
;
8420 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8421 errmsg("cannot alter system column \"%s\"",
8424 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8426 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8427 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8428 colName
, RelationGetRelationName(rel
))));
8429 ReleaseSysCache(tuple
);
8432 * Clear all the missing values if we're rewriting the table, since this
8433 * renders them pointless.
8435 RelationClearMissing(rel
);
8437 /* make sure we don't conflict with later attribute modifications */
8438 CommandCounterIncrement();
8441 * Find everything that depends on the column (constraints, indexes, etc),
8442 * and record enough information to let us recreate the objects after
8445 RememberAllDependentForRebuilding(tab
, AT_SetExpression
, rel
, attnum
, colName
);
8448 * Drop the dependency records of the GENERATED expression, in particular
8449 * its INTERNAL dependency on the column, which would otherwise cause
8450 * dependency.c to refuse to perform the deletion.
8452 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8453 if (!OidIsValid(attrdefoid
))
8454 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8455 RelationGetRelid(rel
), attnum
);
8456 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8458 /* Make above changes visible */
8459 CommandCounterIncrement();
8462 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8463 * safety, but at present we do not expect anything to depend on the
8466 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8469 /* Prepare to store the new expression, in the catalogs */
8470 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8471 rawEnt
->attnum
= attnum
;
8472 rawEnt
->raw_default
= newExpr
;
8473 rawEnt
->missingMode
= false;
8474 rawEnt
->generated
= ATTRIBUTE_GENERATED_STORED
;
8476 /* Store the generated expression */
8477 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8478 false, true, false, NULL
);
8480 /* Make above new expression visible */
8481 CommandCounterIncrement();
8483 /* Prepare for table rewrite */
8484 defval
= (Expr
*) build_column_default(rel
, attnum
);
8486 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
8487 newval
->attnum
= attnum
;
8488 newval
->expr
= expression_planner(defval
);
8489 newval
->is_generated
= true;
8491 tab
->newvals
= lappend(tab
->newvals
, newval
);
8492 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
8494 /* Drop any pg_statistic entry for the column */
8495 RemoveStatistics(RelationGetRelid(rel
), attnum
);
8497 InvokeObjectPostAlterHook(RelationRelationId
,
8498 RelationGetRelid(rel
), attnum
);
8500 ObjectAddressSubSet(address
, RelationRelationId
,
8501 RelationGetRelid(rel
), attnum
);
8506 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8509 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8512 * Reject ONLY if there are child tables. We could implement this, but it
8513 * is a bit complicated. GENERATED clauses must be attached to the column
8514 * definition and cannot be added later like DEFAULT, so if a child table
8515 * has a generation expression that the parent does not have, the child
8516 * column will necessarily be an attislocal column. So to implement ONLY
8517 * here, we'd need extra code to update attislocal of the direct child
8518 * tables, somewhat similar to how DROP COLUMN does it, so that the
8519 * resulting state can be properly dumped and restored.
8522 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8525 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8528 * Cannot drop generation expression from inherited columns.
8533 Form_pg_attribute attTup
;
8535 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8536 if (!HeapTupleIsValid(tuple
))
8538 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8539 errmsg("column \"%s\" of relation \"%s\" does not exist",
8540 cmd
->name
, RelationGetRelationName(rel
))));
8542 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8544 if (attTup
->attinhcount
> 0)
8546 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8547 errmsg("cannot drop generation expression from inherited column")));
8552 * Return the address of the affected column.
8554 static ObjectAddress
8555 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8558 Form_pg_attribute attTup
;
8560 Relation attrelation
;
8562 ObjectAddress address
;
8564 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8565 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8566 if (!HeapTupleIsValid(tuple
))
8568 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8569 errmsg("column \"%s\" of relation \"%s\" does not exist",
8570 colName
, RelationGetRelationName(rel
))));
8572 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8573 attnum
= attTup
->attnum
;
8577 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8578 errmsg("cannot alter system column \"%s\"",
8581 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8585 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8586 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8587 colName
, RelationGetRelationName(rel
))));
8591 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8592 colName
, RelationGetRelationName(rel
))));
8593 heap_freetuple(tuple
);
8594 table_close(attrelation
, RowExclusiveLock
);
8595 return InvalidObjectAddress
;
8600 * Mark the column as no longer generated. (The atthasdef flag needs to
8601 * get cleared too, but RemoveAttrDefault will handle that.)
8603 attTup
->attgenerated
= '\0';
8604 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8606 InvokeObjectPostAlterHook(RelationRelationId
,
8607 RelationGetRelid(rel
),
8609 heap_freetuple(tuple
);
8611 table_close(attrelation
, RowExclusiveLock
);
8614 * Drop the dependency records of the GENERATED expression, in particular
8615 * its INTERNAL dependency on the column, which would otherwise cause
8616 * dependency.c to refuse to perform the deletion.
8618 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8619 if (!OidIsValid(attrdefoid
))
8620 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8621 RelationGetRelid(rel
), attnum
);
8622 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8624 /* Make above changes visible */
8625 CommandCounterIncrement();
8628 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8629 * safety, but at present we do not expect anything to depend on the
8632 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8635 ObjectAddressSubSet(address
, RelationRelationId
,
8636 RelationGetRelid(rel
), attnum
);
8641 * ALTER TABLE ALTER COLUMN SET STATISTICS
8643 * Return value is the address of the modified column
8645 static ObjectAddress
8646 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8649 bool newtarget_default
;
8650 Relation attrelation
;
8653 Form_pg_attribute attrtuple
;
8655 ObjectAddress address
;
8656 Datum repl_val
[Natts_pg_attribute
];
8657 bool repl_null
[Natts_pg_attribute
];
8658 bool repl_repl
[Natts_pg_attribute
];
8661 * We allow referencing columns by numbers only for indexes, since table
8662 * column numbers could contain gaps if columns are later dropped.
8664 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8665 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8668 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8669 errmsg("cannot refer to non-index column by number")));
8671 /* -1 was used in previous versions for the default setting */
8672 if (newValue
&& intVal(newValue
) != -1)
8674 newtarget
= intVal(newValue
);
8675 newtarget_default
= false;
8678 newtarget_default
= true;
8680 if (!newtarget_default
)
8683 * Limit target to a sane range
8688 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8689 errmsg("statistics target %d is too low",
8692 else if (newtarget
> MAX_STATISTICS_TARGET
)
8694 newtarget
= MAX_STATISTICS_TARGET
;
8696 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8697 errmsg("lowering statistics target to %d",
8702 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8706 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8708 if (!HeapTupleIsValid(tuple
))
8710 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8711 errmsg("column \"%s\" of relation \"%s\" does not exist",
8712 colName
, RelationGetRelationName(rel
))));
8716 tuple
= SearchSysCacheAttNum(RelationGetRelid(rel
), colNum
);
8718 if (!HeapTupleIsValid(tuple
))
8720 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8721 errmsg("column number %d of relation \"%s\" does not exist",
8722 colNum
, RelationGetRelationName(rel
))));
8725 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8727 attnum
= attrtuple
->attnum
;
8730 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8731 errmsg("cannot alter system column \"%s\"",
8734 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8735 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8737 if (attnum
> rel
->rd_index
->indnkeyatts
)
8739 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8740 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8741 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8742 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8744 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8745 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8746 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8747 errhint("Alter statistics on table column instead.")));
8750 /* Build new tuple. */
8751 memset(repl_null
, false, sizeof(repl_null
));
8752 memset(repl_repl
, false, sizeof(repl_repl
));
8753 if (!newtarget_default
)
8754 repl_val
[Anum_pg_attribute_attstattarget
- 1] = newtarget
;
8756 repl_null
[Anum_pg_attribute_attstattarget
- 1] = true;
8757 repl_repl
[Anum_pg_attribute_attstattarget
- 1] = true;
8758 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8759 repl_val
, repl_null
, repl_repl
);
8760 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, newtuple
);
8762 InvokeObjectPostAlterHook(RelationRelationId
,
8763 RelationGetRelid(rel
),
8765 ObjectAddressSubSet(address
, RelationRelationId
,
8766 RelationGetRelid(rel
), attnum
);
8768 heap_freetuple(newtuple
);
8770 ReleaseSysCache(tuple
);
8772 table_close(attrelation
, RowExclusiveLock
);
8778 * Return value is the address of the modified column
8780 static ObjectAddress
8781 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8782 bool isReset
, LOCKMODE lockmode
)
8784 Relation attrelation
;
8787 Form_pg_attribute attrtuple
;
8792 ObjectAddress address
;
8793 Datum repl_val
[Natts_pg_attribute
];
8794 bool repl_null
[Natts_pg_attribute
];
8795 bool repl_repl
[Natts_pg_attribute
];
8797 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8799 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8801 if (!HeapTupleIsValid(tuple
))
8803 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8804 errmsg("column \"%s\" of relation \"%s\" does not exist",
8805 colName
, RelationGetRelationName(rel
))));
8806 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8808 attnum
= attrtuple
->attnum
;
8811 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8812 errmsg("cannot alter system column \"%s\"",
8815 /* Generate new proposed attoptions (text array) */
8816 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8818 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8819 castNode(List
, options
), NULL
, NULL
,
8821 /* Validate new options */
8822 (void) attribute_reloptions(newOptions
, true);
8824 /* Build new tuple. */
8825 memset(repl_null
, false, sizeof(repl_null
));
8826 memset(repl_repl
, false, sizeof(repl_repl
));
8827 if (newOptions
!= (Datum
) 0)
8828 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8830 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8831 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8832 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8833 repl_val
, repl_null
, repl_repl
);
8835 /* Update system catalog. */
8836 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8838 InvokeObjectPostAlterHook(RelationRelationId
,
8839 RelationGetRelid(rel
),
8841 ObjectAddressSubSet(address
, RelationRelationId
,
8842 RelationGetRelid(rel
), attnum
);
8844 heap_freetuple(newtuple
);
8846 ReleaseSysCache(tuple
);
8848 table_close(attrelation
, RowExclusiveLock
);
8854 * Helper function for ATExecSetStorage and ATExecSetCompression
8856 * Set the attstorage and/or attcompression fields for index columns
8857 * associated with the specified table column.
8860 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
8862 bool setstorage
, char newstorage
,
8863 bool setcompression
, char newcompression
,
8868 foreach(lc
, RelationGetIndexList(rel
))
8870 Oid indexoid
= lfirst_oid(lc
);
8872 AttrNumber indattnum
= 0;
8875 indrel
= index_open(indexoid
, lockmode
);
8877 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
8879 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
8888 index_close(indrel
, lockmode
);
8892 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
8894 if (HeapTupleIsValid(tuple
))
8896 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8899 attrtuple
->attstorage
= newstorage
;
8902 attrtuple
->attcompression
= newcompression
;
8904 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8906 InvokeObjectPostAlterHook(RelationRelationId
,
8907 RelationGetRelid(rel
),
8910 heap_freetuple(tuple
);
8913 index_close(indrel
, lockmode
);
8918 * ALTER TABLE ALTER COLUMN SET STORAGE
8920 * Return value is the address of the modified column
8922 static ObjectAddress
8923 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
8925 Relation attrelation
;
8927 Form_pg_attribute attrtuple
;
8929 ObjectAddress address
;
8931 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8933 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8935 if (!HeapTupleIsValid(tuple
))
8937 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8938 errmsg("column \"%s\" of relation \"%s\" does not exist",
8939 colName
, RelationGetRelationName(rel
))));
8940 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8942 attnum
= attrtuple
->attnum
;
8945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8946 errmsg("cannot alter system column \"%s\"",
8949 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
8951 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8953 InvokeObjectPostAlterHook(RelationRelationId
,
8954 RelationGetRelid(rel
),
8958 * Apply the change to indexes as well (only for simple index columns,
8959 * matching behavior of index.c ConstructTupleDescriptor()).
8961 SetIndexStorageProperties(rel
, attrelation
, attnum
,
8962 true, attrtuple
->attstorage
,
8966 heap_freetuple(tuple
);
8968 table_close(attrelation
, RowExclusiveLock
);
8970 ObjectAddressSubSet(address
, RelationRelationId
,
8971 RelationGetRelid(rel
), attnum
);
8977 * ALTER TABLE DROP COLUMN
8979 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8980 * because we have to decide at runtime whether to recurse or not depending
8981 * on whether attinhcount goes to zero or not. (We can't check this in a
8982 * static pre-pass because it won't handle multiple inheritance situations
8986 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
8987 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
8988 AlterTableUtilityContext
*context
)
8990 if (rel
->rd_rel
->reloftype
&& !recursing
)
8992 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
8993 errmsg("cannot drop column from typed table")));
8995 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
8996 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
8999 cmd
->recurse
= true;
9003 * Drops column 'colName' from relation 'rel' and returns the address of the
9004 * dropped column. The column is also dropped (or marked as no longer
9005 * inherited from relation) from the relation's inheritance children, if any.
9007 * In the recursive invocations for inheritance child relations, instead of
9008 * dropping the column directly (if to be dropped at all), its object address
9009 * is added to 'addrs', which must be non-NULL in such invocations. All
9010 * columns are dropped at the same time after all the children have been
9011 * checked recursively.
9013 static ObjectAddress
9014 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
9015 DropBehavior behavior
,
9016 bool recurse
, bool recursing
,
9017 bool missing_ok
, LOCKMODE lockmode
,
9018 ObjectAddresses
*addrs
)
9021 Form_pg_attribute targetatt
;
9024 ObjectAddress object
;
9027 /* At top level, permission check was done in ATPrepCmd, else do it */
9029 ATSimplePermissions(AT_DropColumn
, rel
,
9030 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
9032 /* Initialize addrs on the first invocation */
9033 Assert(!recursing
|| addrs
!= NULL
);
9035 /* since this function recurses, it could be driven to stack overflow */
9036 check_stack_depth();
9039 addrs
= new_object_addresses();
9042 * get the number of the attribute
9044 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
9045 if (!HeapTupleIsValid(tuple
))
9050 (errcode(ERRCODE_UNDEFINED_COLUMN
),
9051 errmsg("column \"%s\" of relation \"%s\" does not exist",
9052 colName
, RelationGetRelationName(rel
))));
9057 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9058 colName
, RelationGetRelationName(rel
))));
9059 return InvalidObjectAddress
;
9062 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9064 attnum
= targetatt
->attnum
;
9066 /* Can't drop a system attribute */
9069 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9070 errmsg("cannot drop system column \"%s\"",
9074 * Don't drop inherited columns, unless recursing (presumably from a drop
9075 * of the parent column)
9077 if (targetatt
->attinhcount
> 0 && !recursing
)
9079 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9080 errmsg("cannot drop inherited column \"%s\"",
9084 * Don't drop columns used in the partition key, either. (If we let this
9085 * go through, the key column's dependencies would cause a cascaded drop
9086 * of the whole table, which is surely not what the user expected.)
9088 if (has_partition_attrs(rel
,
9089 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
9092 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9093 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9094 colName
, RelationGetRelationName(rel
))));
9096 ReleaseSysCache(tuple
);
9099 * Propagate to children as appropriate. Unlike most other ALTER
9100 * routines, we have to do this one level of recursion at a time; we can't
9101 * use find_all_inheritors to do it in one pass.
9104 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9112 * In case of a partitioned table, the column must be dropped from the
9113 * partitions as well.
9115 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
9117 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9118 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9119 errhint("Do not specify the ONLY keyword.")));
9121 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
9122 foreach(child
, children
)
9124 Oid childrelid
= lfirst_oid(child
);
9126 Form_pg_attribute childatt
;
9128 /* find_inheritance_children already got lock */
9129 childrel
= table_open(childrelid
, NoLock
);
9130 CheckAlterTableIsSafe(childrel
);
9132 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
9133 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
9134 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
9135 colName
, childrelid
);
9136 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9138 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
9139 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
9140 childrelid
, colName
);
9145 * If the child column has other definition sources, just
9146 * decrement its inheritance count; if not, recurse to delete
9149 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
9151 /* Time to delete this child column, too */
9152 ATExecDropColumn(wqueue
, childrel
, colName
,
9153 behavior
, true, true,
9154 false, lockmode
, addrs
);
9158 /* Child column must survive my deletion */
9159 childatt
->attinhcount
--;
9161 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9163 /* Make update visible */
9164 CommandCounterIncrement();
9170 * If we were told to drop ONLY in this table (no recursion),
9171 * we need to mark the inheritors' attributes as locally
9172 * defined rather than inherited.
9174 childatt
->attinhcount
--;
9175 childatt
->attislocal
= true;
9177 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9179 /* Make update visible */
9180 CommandCounterIncrement();
9183 heap_freetuple(tuple
);
9185 table_close(childrel
, NoLock
);
9187 table_close(attr_rel
, RowExclusiveLock
);
9190 /* Add object to delete */
9191 object
.classId
= RelationRelationId
;
9192 object
.objectId
= RelationGetRelid(rel
);
9193 object
.objectSubId
= attnum
;
9194 add_exact_object_address(&object
, addrs
);
9198 /* Recursion has ended, drop everything that was collected */
9199 performMultipleDeletions(addrs
, behavior
, 0);
9200 free_object_addresses(addrs
);
9207 * Prepare to add a primary key on table, by adding not-null constraints
9211 ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
9212 bool recurse
, LOCKMODE lockmode
,
9213 AlterTableUtilityContext
*context
)
9216 Constraint
*pkconstr
;
9218 pkconstr
= castNode(Constraint
, cmd
->def
);
9219 if (pkconstr
->contype
!= CONSTR_PRIMARY
)
9223 * If not recursing, we must ensure that all children have a NOT NULL
9224 * constraint on the columns, and error out if not.
9230 children
= find_inheritance_children(RelationGetRelid(rel
),
9232 foreach_oid(childrelid
, children
)
9234 foreach(lc
, pkconstr
->keys
)
9237 Form_pg_attribute attrForm
;
9238 char *attname
= strVal(lfirst(lc
));
9240 tup
= SearchSysCacheAttName(childrelid
, attname
);
9242 elog(ERROR
, "cache lookup failed for attribute %s of relation %u",
9243 attname
, childrelid
);
9244 attrForm
= (Form_pg_attribute
) GETSTRUCT(tup
);
9245 if (!attrForm
->attnotnull
)
9247 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9248 attname
, get_rel_name(childrelid
)));
9249 ReleaseSysCache(tup
);
9254 /* Insert not-null constraints in the queue for the PK columns */
9255 foreach(lc
, pkconstr
->keys
)
9257 AlterTableCmd
*newcmd
;
9258 Constraint
*nnconstr
;
9260 nnconstr
= makeNotNullConstraint(lfirst(lc
));
9262 newcmd
= makeNode(AlterTableCmd
);
9263 newcmd
->subtype
= AT_AddConstraint
;
9264 newcmd
->recurse
= true;
9265 newcmd
->def
= (Node
*) nnconstr
;
9267 ATPrepCmd(wqueue
, rel
, newcmd
, true, false, lockmode
, context
);
9272 * ALTER TABLE ADD INDEX
9274 * There is no such command in the grammar, but parse_utilcmd.c converts
9275 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9276 * us schedule creation of the index at the appropriate time during ALTER.
9278 * Return value is the address of the new index.
9280 static ObjectAddress
9281 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
9282 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9287 ObjectAddress address
;
9289 Assert(IsA(stmt
, IndexStmt
));
9290 Assert(!stmt
->concurrent
);
9292 /* The IndexStmt has already been through transformIndexStmt */
9293 Assert(stmt
->transformed
);
9295 /* suppress schema rights check when rebuilding existing index */
9296 check_rights
= !is_rebuild
;
9297 /* skip index build if phase 3 will do it or we're reusing an old one */
9298 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
9299 /* suppress notices when rebuilding existing index */
9302 address
= DefineIndex(RelationGetRelid(rel
),
9304 InvalidOid
, /* no predefined OID */
9305 InvalidOid
, /* no parent index */
9306 InvalidOid
, /* no parent constraint */
9307 -1, /* total_parts unknown */
9308 true, /* is_alter_table */
9310 false, /* check_not_in_use - we did it already */
9315 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9316 * new index instead of building from scratch. Restore associated fields.
9317 * This may store InvalidSubTransactionId in both fields, in which case
9318 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9319 * this after the CCI that made catalog rows visible to any rebuild. The
9320 * DROP of the old edition of this index will have scheduled the storage
9321 * for deletion at commit, so cancel that pending deletion.
9323 if (RelFileNumberIsValid(stmt
->oldNumber
))
9325 Relation irel
= index_open(address
.objectId
, NoLock
);
9327 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
9328 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
9329 RelationPreserveStorage(irel
->rd_locator
, true);
9330 index_close(irel
, NoLock
);
9337 * ALTER TABLE ADD STATISTICS
9339 * This is no such command in the grammar, but we use this internally to add
9340 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9341 * column type change.
9343 static ObjectAddress
9344 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
9345 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9347 ObjectAddress address
;
9349 Assert(IsA(stmt
, CreateStatsStmt
));
9351 /* The CreateStatsStmt has already been through transformStatsStmt */
9352 Assert(stmt
->transformed
);
9354 address
= CreateStatistics(stmt
);
9360 * ALTER TABLE ADD CONSTRAINT USING INDEX
9362 * Returns the address of the new constraint.
9364 static ObjectAddress
9365 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
9366 IndexStmt
*stmt
, LOCKMODE lockmode
)
9368 Oid index_oid
= stmt
->indexOid
;
9371 IndexInfo
*indexInfo
;
9372 char *constraintName
;
9373 char constraintType
;
9374 ObjectAddress address
;
9377 Assert(IsA(stmt
, IndexStmt
));
9378 Assert(OidIsValid(index_oid
));
9379 Assert(stmt
->isconstraint
);
9382 * Doing this on partitioned tables is not a simple feature to implement,
9383 * so let's punt for now.
9385 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9387 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9388 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9390 indexRel
= index_open(index_oid
, AccessShareLock
);
9392 indexName
= pstrdup(RelationGetRelationName(indexRel
));
9394 indexInfo
= BuildIndexInfo(indexRel
);
9396 /* this should have been checked at parse time */
9397 if (!indexInfo
->ii_Unique
)
9398 elog(ERROR
, "index \"%s\" is not unique", indexName
);
9401 * Determine name to assign to constraint. We require a constraint to
9402 * have the same name as the underlying index; therefore, use the index's
9403 * existing name as the default constraint name, and if the user
9404 * explicitly gives some other name for the constraint, rename the index
9407 constraintName
= stmt
->idxname
;
9408 if (constraintName
== NULL
)
9409 constraintName
= indexName
;
9410 else if (strcmp(constraintName
, indexName
) != 0)
9413 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9414 indexName
, constraintName
)));
9415 RenameRelationInternal(index_oid
, constraintName
, false, true);
9418 /* Extra checks needed if making primary key */
9420 index_check_primary_key(rel
, indexInfo
, true, stmt
);
9422 /* Note we currently don't support EXCLUSION constraints here */
9424 constraintType
= CONSTRAINT_PRIMARY
;
9426 constraintType
= CONSTRAINT_UNIQUE
;
9428 /* Create the catalog entries for the constraint */
9429 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
9430 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
9431 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
9432 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
9433 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
9435 address
= index_constraint_create(rel
,
9442 allowSystemTableMods
,
9443 false); /* is_internal */
9445 index_close(indexRel
, NoLock
);
9451 * ALTER TABLE ADD CONSTRAINT
9453 * Return value is the address of the new constraint; if no constraint was
9454 * added, InvalidObjectAddress is returned.
9456 static ObjectAddress
9457 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9458 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
9461 ObjectAddress address
= InvalidObjectAddress
;
9463 Assert(IsA(newConstraint
, Constraint
));
9466 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9467 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9470 switch (newConstraint
->contype
)
9473 case CONSTR_NOTNULL
:
9475 ATAddCheckNNConstraint(wqueue
, tab
, rel
,
9476 newConstraint
, recurse
, false, is_readd
,
9480 case CONSTR_FOREIGN
:
9483 * Assign or validate constraint name
9485 if (newConstraint
->conname
)
9487 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9488 RelationGetRelid(rel
),
9489 newConstraint
->conname
))
9491 (errcode(ERRCODE_DUPLICATE_OBJECT
),
9492 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9493 newConstraint
->conname
,
9494 RelationGetRelationName(rel
))));
9497 newConstraint
->conname
=
9498 ChooseConstraintName(RelationGetRelationName(rel
),
9499 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
9501 RelationGetNamespace(rel
),
9504 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
9511 elog(ERROR
, "unrecognized constraint type: %d",
9512 (int) newConstraint
->contype
);
9519 * Generate the column-name portion of the constraint name for a new foreign
9520 * key given the list of column names that reference the referenced
9521 * table. This will be passed to ChooseConstraintName along with the parent
9522 * table name and the "fkey" suffix.
9524 * We know that less than NAMEDATALEN characters will actually be used, so we
9525 * can truncate the result once we've generated that many.
9527 * XXX see also ChooseExtendedStatisticNameAddition and
9528 * ChooseIndexNameAddition.
9531 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
9533 char buf
[NAMEDATALEN
* 2];
9538 foreach(lc
, colnames
)
9540 const char *name
= strVal(lfirst(lc
));
9543 buf
[buflen
++] = '_'; /* insert _ between names */
9546 * At this point we have buflen <= NAMEDATALEN. name should be less
9547 * than NAMEDATALEN already, but use strlcpy for paranoia.
9549 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
9550 buflen
+= strlen(buf
+ buflen
);
9551 if (buflen
>= NAMEDATALEN
)
9554 return pstrdup(buf
);
9558 * Add a check or not-null constraint to a single table and its children.
9559 * Returns the address of the constraint added to the parent relation,
9560 * if one gets added, or InvalidObjectAddress otherwise.
9562 * Subroutine for ATExecAddConstraint.
9564 * We must recurse to child tables during execution, rather than using
9565 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9566 * constraints *must* be given the same name, else they won't be seen as
9567 * related later. If the user didn't explicitly specify a name, then
9568 * AddRelationNewConstraints would normally assign different names to the
9569 * child constraints. To fix that, we must capture the name assigned at
9570 * the parent table and pass that down.
9572 static ObjectAddress
9573 ATAddCheckNNConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9574 Constraint
*constr
, bool recurse
, bool recursing
,
9575 bool is_readd
, LOCKMODE lockmode
)
9581 ObjectAddress address
= InvalidObjectAddress
;
9583 /* Guard against stack overflow due to overly deep inheritance tree. */
9584 check_stack_depth();
9586 /* At top level, permission check was done in ATPrepCmd, else do it */
9588 ATSimplePermissions(AT_AddConstraint
, rel
,
9589 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
9592 * Call AddRelationNewConstraints to do the work, making sure it works on
9593 * a copy of the Constraint so transformExpr can't modify the original. It
9594 * returns a list of cooked constraints.
9596 * If the constraint ends up getting merged with a pre-existing one, it's
9597 * omitted from the returned list, which is what we want: we do not need
9598 * to do any validation work. That can only happen at child tables,
9599 * though, since we disallow merging at the top level.
9601 newcons
= AddRelationNewConstraints(rel
, NIL
,
9602 list_make1(copyObject(constr
)),
9603 recursing
|| is_readd
, /* allow_merge */
9604 !recursing
, /* is_local */
9605 is_readd
, /* is_internal */
9606 NULL
); /* queryString not available
9609 /* we don't expect more than one constraint here */
9610 Assert(list_length(newcons
) <= 1);
9612 /* Add each to-be-validated constraint to Phase 3's queue */
9613 foreach(lcon
, newcons
)
9615 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9617 if (!ccon
->skip_validation
&& ccon
->contype
!= CONSTR_NOTNULL
)
9619 NewConstraint
*newcon
;
9621 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9622 newcon
->name
= ccon
->name
;
9623 newcon
->contype
= ccon
->contype
;
9624 newcon
->qual
= ccon
->expr
;
9626 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9629 /* Save the actually assigned name if it was defaulted */
9630 if (constr
->conname
== NULL
)
9631 constr
->conname
= ccon
->name
;
9634 * If adding a not-null constraint, set the pg_attribute flag and tell
9635 * phase 3 to verify existing rows, if needed.
9637 if (constr
->contype
== CONSTR_NOTNULL
)
9638 set_attnotnull(wqueue
, rel
, ccon
->attnum
, lockmode
);
9640 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9643 /* At this point we must have a locked-down name to use */
9644 Assert(newcons
== NIL
|| constr
->conname
!= NULL
);
9646 /* Advance command counter in case same table is visited multiple times */
9647 CommandCounterIncrement();
9650 * If the constraint got merged with an existing constraint, we're done.
9651 * We mustn't recurse to child tables in this case, because they've
9652 * already got the constraint, and visiting them again would lead to an
9653 * incorrect value for coninhcount.
9659 * If adding a NO INHERIT constraint, no need to find our children.
9661 if (constr
->is_no_inherit
)
9665 * Propagate to children as appropriate. Unlike most other ALTER
9666 * routines, we have to do this one level of recursion at a time; we can't
9667 * use find_all_inheritors to do it in one pass.
9670 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9673 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9674 * constraint creation only if there are no children currently. Error out
9677 if (!recurse
&& children
!= NIL
)
9679 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9680 errmsg("constraint must be added to child tables too")));
9683 * Recurse to create the constraint on each child.
9685 foreach(child
, children
)
9687 Oid childrelid
= lfirst_oid(child
);
9689 AlteredTableInfo
*childtab
;
9691 /* find_inheritance_children already got lock */
9692 childrel
= table_open(childrelid
, NoLock
);
9693 CheckAlterTableIsSafe(childrel
);
9695 /* Find or create work queue entry for this table */
9696 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9698 /* Recurse to this child */
9699 ATAddCheckNNConstraint(wqueue
, childtab
, childrel
,
9700 constr
, recurse
, true, is_readd
, lockmode
);
9702 table_close(childrel
, NoLock
);
9709 * Add a foreign-key constraint to a single table; return the new constraint's
9712 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9713 * lock on the rel, and have done appropriate validity checks for it.
9714 * We do permissions checks here, however.
9716 * When the referenced or referencing tables (or both) are partitioned,
9717 * multiple pg_constraint rows are required -- one for each partitioned table
9718 * and each partition on each side (fortunately, not one for every combination
9719 * thereof). We also need action triggers on each leaf partition on the
9720 * referenced side, and check triggers on each leaf partition on the
9723 static ObjectAddress
9724 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9725 Constraint
*fkconstraint
,
9726 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9729 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9730 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9731 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9732 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9733 Oid pkcolloid
[INDEX_MAX_KEYS
] = {0};
9734 Oid fkcolloid
[INDEX_MAX_KEYS
] = {0};
9735 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9736 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9737 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9738 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9739 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9741 bool pk_has_without_overlaps
;
9748 ObjectAddress address
;
9749 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9752 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9753 * delete rows out from under us.
9755 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9756 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9758 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9761 * Validity checks (permission checks wait till we have the column
9764 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9768 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9769 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9770 RelationGetRelationName(rel
),
9771 RelationGetRelationName(pkrel
))));
9772 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9774 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9775 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9776 RelationGetRelationName(rel
),
9777 RelationGetRelationName(pkrel
)),
9778 errdetail("This feature is not yet supported on partitioned tables.")));
9781 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9782 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9784 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9785 errmsg("referenced relation \"%s\" is not a table",
9786 RelationGetRelationName(pkrel
))));
9788 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9790 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9791 errmsg("permission denied: \"%s\" is a system catalog",
9792 RelationGetRelationName(pkrel
))));
9795 * References from permanent or unlogged tables to temp tables, and from
9796 * permanent tables to unlogged tables, are disallowed because the
9797 * referenced data can vanish out from under us. References from temp
9798 * tables to any other table type are also disallowed, because other
9799 * backends might need to run the RI triggers on the perm table, but they
9800 * can't reliably see tuples in the local buffers of other backends.
9802 switch (rel
->rd_rel
->relpersistence
)
9804 case RELPERSISTENCE_PERMANENT
:
9805 if (!RelationIsPermanent(pkrel
))
9807 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9808 errmsg("constraints on permanent tables may reference only permanent tables")));
9810 case RELPERSISTENCE_UNLOGGED
:
9811 if (!RelationIsPermanent(pkrel
)
9812 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9814 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9815 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9817 case RELPERSISTENCE_TEMP
:
9818 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9820 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9821 errmsg("constraints on temporary tables may reference only temporary tables")));
9822 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
9824 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9825 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9830 * Look up the referencing attributes to make sure they exist, and record
9831 * their attnums and type and collation OIDs.
9833 numfks
= transformColumnNameList(RelationGetRelid(rel
),
9834 fkconstraint
->fk_attrs
,
9835 fkattnum
, fktypoid
, fkcolloid
);
9836 with_period
= fkconstraint
->fk_with_period
|| fkconstraint
->pk_with_period
;
9837 if (with_period
&& !fkconstraint
->fk_with_period
)
9839 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9840 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9842 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
9843 fkconstraint
->fk_del_set_cols
,
9844 fkdelsetcols
, NULL
, NULL
);
9845 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
9846 numfkdelsetcols
, fkdelsetcols
,
9847 fkconstraint
->fk_del_set_cols
);
9850 * If the attribute list for the referenced table was omitted, lookup the
9851 * definition of the primary key and use it. Otherwise, validate the
9852 * supplied attribute list. In either case, discover the index OID and
9853 * index opclasses, and the attnums and type and collation OIDs of the
9856 if (fkconstraint
->pk_attrs
== NIL
)
9858 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
9859 &fkconstraint
->pk_attrs
,
9860 pkattnum
, pktypoid
, pkcolloid
,
9861 opclasses
, &pk_has_without_overlaps
);
9863 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9864 if (pk_has_without_overlaps
&& !fkconstraint
->fk_with_period
)
9866 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9867 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9871 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
9872 fkconstraint
->pk_attrs
,
9873 pkattnum
, pktypoid
, pkcolloid
);
9875 /* Since we got pk_attrs, one should be a period. */
9876 if (with_period
&& !fkconstraint
->pk_with_period
)
9878 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9879 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9881 /* Look for an index matching the column list */
9882 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
9883 with_period
, opclasses
, &pk_has_without_overlaps
);
9887 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9890 if (pk_has_without_overlaps
&& !with_period
)
9892 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9893 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9896 * Now we can check permissions.
9898 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
9901 * Check some things for generated columns.
9903 for (i
= 0; i
< numfks
; i
++)
9905 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
9910 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9912 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9913 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
9914 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
9916 (errcode(ERRCODE_SYNTAX_ERROR
),
9917 errmsg("invalid %s action for foreign key constraint containing generated column",
9919 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
9920 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
9922 (errcode(ERRCODE_SYNTAX_ERROR
),
9923 errmsg("invalid %s action for foreign key constraint containing generated column",
9929 * Some actions are currently unsupported for foreign keys using PERIOD.
9931 if (fkconstraint
->fk_with_period
)
9933 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
||
9934 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9935 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
)
9937 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9938 errmsg("unsupported %s action for foreign key constraint using PERIOD",
9941 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_CASCADE
||
9942 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
9943 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
9945 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9946 errmsg("unsupported %s action for foreign key constraint using PERIOD",
9951 * Look up the equality operators to use in the constraint.
9953 * Note that we have to be careful about the difference between the actual
9954 * PK column type and the opclass' declared input type, which might be
9955 * only binary-compatible with it. The declared opcintype is the right
9956 * thing to probe pg_amop with.
9958 if (numfks
!= numpks
)
9960 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9961 errmsg("number of referencing and referenced columns for foreign key disagree")));
9964 * On the strength of a previous constraint, we might avoid scanning
9965 * tables to validate this one. See below.
9967 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
9968 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
9970 for (i
= 0; i
< numpks
; i
++)
9972 Oid pktype
= pktypoid
[i
];
9973 Oid fktype
= fktypoid
[i
];
9975 Oid pkcoll
= pkcolloid
[i
];
9976 Oid fkcoll
= fkcolloid
[i
];
9978 Form_pg_opclass cla_tup
;
9988 /* We need several fields out of the pg_opclass entry */
9989 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
9990 if (!HeapTupleIsValid(cla_ht
))
9991 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
9992 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
9993 amid
= cla_tup
->opcmethod
;
9994 opfamily
= cla_tup
->opcfamily
;
9995 opcintype
= cla_tup
->opcintype
;
9996 ReleaseSysCache(cla_ht
);
10000 CompareType cmptype
;
10001 bool for_overlaps
= with_period
&& i
== numpks
- 1;
10004 * GiST indexes are required to support temporal foreign keys
10005 * because they combine equals and overlaps.
10007 if (amid
!= GIST_AM_OID
)
10008 elog(ERROR
, "only GiST indexes are supported for temporal foreign keys");
10010 cmptype
= for_overlaps
? COMPARE_OVERLAP
: COMPARE_EQ
;
10013 * An opclass can use whatever strategy numbers it wants, so we
10014 * ask the opclass what number it actually uses instead of our RT*
10017 eqstrategy
= GistTranslateStratnum(opclasses
[i
], cmptype
);
10018 if (eqstrategy
== InvalidStrategy
)
10022 tuple
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
10023 if (!HeapTupleIsValid(tuple
))
10024 elog(ERROR
, "cache lookup failed for operator class %u", opclasses
[i
]);
10027 errcode(ERRCODE_UNDEFINED_OBJECT
),
10029 ? errmsg("could not identify an overlaps operator for foreign key")
10030 : errmsg("could not identify an equality operator for foreign key"),
10031 errdetail("Could not translate compare type %d for operator class \"%s\" for access method \"%s\".",
10032 cmptype
, NameStr(((Form_pg_opclass
) GETSTRUCT(tuple
))->opcname
), "gist"));
10038 * Check it's a btree; currently this can never fail since no
10039 * other index AMs support unique indexes. If we ever did have
10040 * other types of unique indexes, we'd need a way to determine
10041 * which operator strategy number is equality. (We could use
10042 * something like GistTranslateStratnum.)
10044 if (amid
!= BTREE_AM_OID
)
10045 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
10046 eqstrategy
= BTEqualStrategyNumber
;
10050 * There had better be a primary equality operator for the index.
10051 * We'll use it for PK = PK comparisons.
10053 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
10056 if (!OidIsValid(ppeqop
))
10057 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
10058 eqstrategy
, opcintype
, opcintype
, opfamily
);
10061 * Are there equality operators that take exactly the FK type? Assume
10062 * we should look through any domain here.
10064 fktyped
= getBaseType(fktype
);
10066 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
10068 if (OidIsValid(pfeqop
))
10070 pfeqop_right
= fktyped
;
10071 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
10076 /* keep compiler quiet */
10077 pfeqop_right
= InvalidOid
;
10078 ffeqop
= InvalidOid
;
10081 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10084 * Otherwise, look for an implicit cast from the FK type to the
10085 * opcintype, and if found, use the primary equality operator.
10086 * This is a bit tricky because opcintype might be a polymorphic
10087 * type such as ANYARRAY or ANYENUM; so what we have to test is
10088 * whether the two actual column types can be concurrently cast to
10089 * that type. (Otherwise, we'd fail to reject combinations such
10090 * as int[] and point[].)
10092 Oid input_typeids
[2];
10093 Oid target_typeids
[2];
10095 input_typeids
[0] = pktype
;
10096 input_typeids
[1] = fktype
;
10097 target_typeids
[0] = opcintype
;
10098 target_typeids
[1] = opcintype
;
10099 if (can_coerce_type(2, input_typeids
, target_typeids
,
10100 COERCION_IMPLICIT
))
10102 pfeqop
= ffeqop
= ppeqop
;
10103 pfeqop_right
= opcintype
;
10107 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10109 (errcode(ERRCODE_DATATYPE_MISMATCH
),
10110 errmsg("foreign key constraint \"%s\" cannot be implemented",
10111 fkconstraint
->conname
),
10112 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10113 "are of incompatible types: %s and %s.",
10114 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
10115 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
10116 format_type_be(fktype
),
10117 format_type_be(pktype
))));
10120 * This shouldn't be possible, but better check to make sure we have a
10121 * consistent state for the check below.
10123 if ((OidIsValid(pkcoll
) && !OidIsValid(fkcoll
)) || (!OidIsValid(pkcoll
) && OidIsValid(fkcoll
)))
10124 elog(ERROR
, "key columns are not both collatable");
10126 if (OidIsValid(pkcoll
) && OidIsValid(fkcoll
))
10131 pkcolldet
= get_collation_isdeterministic(pkcoll
);
10132 fkcolldet
= get_collation_isdeterministic(fkcoll
);
10135 * SQL requires that both collations are the same. This is
10136 * because we need a consistent notion of equality on both
10137 * columns. We relax this by allowing different collations if
10138 * they are both deterministic. (This is also for backward
10139 * compatibility, because PostgreSQL has always allowed this.)
10141 if ((!pkcolldet
|| !fkcolldet
) && pkcoll
!= fkcoll
)
10143 (errcode(ERRCODE_COLLATION_MISMATCH
),
10144 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint
->conname
),
10145 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10146 "have incompatible collations: \"%s\" and \"%s\". "
10147 "If either collation is nondeterministic, then both collations have to be the same.",
10148 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
10149 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
10150 get_collation_name(fkcoll
),
10151 get_collation_name(pkcoll
))));
10157 * When a pfeqop changes, revalidate the constraint. We could
10158 * permit intra-opfamily changes, but that adds subtle complexity
10159 * without any concrete benefit for core types. We need not
10160 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10162 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
10163 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
10170 CoercionPathType old_pathtype
;
10171 CoercionPathType new_pathtype
;
10176 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
10180 * Identify coercion pathways from each of the old and new FK-side
10181 * column types to the right (foreign) operand type of the pfeqop.
10182 * We may assume that pg_constraint.conkey is not changing.
10184 old_fktype
= attr
->atttypid
;
10185 new_fktype
= fktype
;
10186 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
10188 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
10191 old_fkcoll
= attr
->attcollation
;
10192 new_fkcoll
= fkcoll
;
10195 * Upon a change to the cast from the FK column to its pfeqop
10196 * operand, revalidate the constraint. For this evaluation, a
10197 * binary coercion cast is equivalent to no cast at all. While
10198 * type implementors should design implicit casts with an eye
10199 * toward consistency of operations like equality, we cannot
10200 * assume here that they have done so.
10202 * A function with a polymorphic argument could change behavior
10203 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10204 * when the cast destination is polymorphic, we only avoid
10205 * revalidation if the input type has not changed at all. Given
10206 * just the core data types and operator classes, this requirement
10207 * prevents no would-be optimizations.
10209 * If the cast converts from a base type to a domain thereon, then
10210 * that domain type must be the opcintype of the unique index.
10211 * Necessarily, the primary key column must then be of the domain
10212 * type. Since the constraint was previously valid, all values on
10213 * the foreign side necessarily exist on the primary side and in
10214 * turn conform to the domain. Consequently, we need not treat
10215 * domains specially here.
10217 * If the collation changes, revalidation is required, unless both
10218 * collations are deterministic, because those share the same
10219 * notion of equality (because texteq reduces to bitwise
10222 * We need not directly consider the PK type. It's necessarily
10223 * binary coercible to the opcintype of the unique index column,
10224 * and ri_triggers.c will only deal with PK datums in terms of
10225 * that opcintype. Changing the opcintype also changes pfeqop.
10227 old_check_ok
= (new_pathtype
== old_pathtype
&&
10228 new_castfunc
== old_castfunc
&&
10229 (!IsPolymorphicType(pfeqop_right
) ||
10230 new_fktype
== old_fktype
) &&
10231 (new_fkcoll
== old_fkcoll
||
10232 (get_collation_isdeterministic(old_fkcoll
) && get_collation_isdeterministic(new_fkcoll
))));
10235 pfeqoperators
[i
] = pfeqop
;
10236 ppeqoperators
[i
] = ppeqop
;
10237 ffeqoperators
[i
] = ffeqop
;
10241 * For FKs with PERIOD we need additional operators to check whether the
10242 * referencing row's range is contained by the aggregated ranges of the
10243 * referenced row(s). For rangetypes and multirangetypes this is
10244 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10245 * support for now. FKs will look these up at "runtime", but we should
10246 * make sure the lookup works here, even if we don't use the values.
10251 Oid aggedperiodoperoid
;
10253 FindFKPeriodOpers(opclasses
[numpks
- 1], &periodoperoid
, &aggedperiodoperoid
);
10256 /* First, create the constraint catalog entry itself. */
10257 address
= addFkConstraint(addFkBothSides
,
10258 fkconstraint
->conname
, fkconstraint
, rel
, pkrel
,
10260 InvalidOid
, /* no parent constraint */
10272 /* Next process the action triggers at the referenced side and recurse */
10273 addFkRecurseReferenced(fkconstraint
, rel
, pkrel
,
10285 InvalidOid
, InvalidOid
,
10288 /* Lastly create the check triggers at the referencing side and recurse */
10289 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
10302 InvalidOid
, InvalidOid
,
10306 * Done. Close pk table, but keep lock until we've committed.
10308 table_close(pkrel
, NoLock
);
10314 * validateFkOnDeleteSetColumns
10315 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10316 * column lists are valid.
10319 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
10320 int numfksetcols
, const int16
*fksetcolsattnums
,
10323 for (int i
= 0; i
< numfksetcols
; i
++)
10325 int16 setcol_attnum
= fksetcolsattnums
[i
];
10328 for (int j
= 0; j
< numfks
; j
++)
10330 if (fkattnums
[j
] == setcol_attnum
)
10339 char *col
= strVal(list_nth(fksetcols
, i
));
10342 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
10343 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
10350 * Install pg_constraint entries to implement a foreign key constraint.
10351 * Caller must separately invoke addFkRecurseReferenced and
10352 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10353 * and (for partitioned tables) recurse to partitions.
10355 * fkside: the side of the FK (or both) to create. Caller should
10356 * call addFkRecurseReferenced if this is addFkReferencedSide,
10357 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10359 * constraintname: the base name for the constraint being added,
10360 * copied to fkconstraint->conname if the latter is not set
10361 * fkconstraint: the constraint being added
10362 * rel: the root referencing relation
10363 * pkrel: the referenced relation; might be a partition, if recursing
10364 * indexOid: the OID of the index (on pkrel) implementing this constraint
10365 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10366 * top-level constraint
10367 * numfks: the number of columns in the foreign key
10368 * pkattnum: the attnum array of referenced attributes
10369 * fkattnum: the attnum array of referencing attributes
10370 * pf/pp/ffeqoperators: OID array of operators between columns
10371 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10373 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10374 * NULL/DEFAULT clause
10375 * with_period: true if this is a temporal FK
10377 static ObjectAddress
10378 addFkConstraint(addFkConstraintSides fkside
,
10379 char *constraintname
, Constraint
*fkconstraint
,
10380 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10381 int numfks
, int16
*pkattnum
,
10382 int16
*fkattnum
, Oid
*pfeqoperators
, Oid
*ppeqoperators
,
10383 Oid
*ffeqoperators
, int numfkdelsetcols
, int16
*fkdelsetcols
,
10384 bool is_internal
, bool with_period
)
10386 ObjectAddress address
;
10394 * Verify relkind for each referenced partition. At the top level, this
10395 * is redundant with a previous check, but we need it when recursing.
10397 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
10398 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
10400 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10401 errmsg("referenced relation \"%s\" is not a table",
10402 RelationGetRelationName(pkrel
))));
10405 * Caller supplies us with a constraint name; however, it may be used in
10406 * this partition, so come up with a different one in that case.
10408 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10409 RelationGetRelid(rel
),
10411 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
10412 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10414 RelationGetNamespace(rel
), NIL
);
10416 conname
= constraintname
;
10418 if (fkconstraint
->conname
== NULL
)
10419 fkconstraint
->conname
= pstrdup(conname
);
10421 if (OidIsValid(parentConstr
))
10423 conislocal
= false;
10425 connoinherit
= false;
10433 * always inherit for partitioned tables, never for legacy inheritance
10435 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
10439 * Record the FK constraint in pg_constraint.
10441 constrOid
= CreateConstraintEntry(conname
,
10442 RelationGetNamespace(rel
),
10443 CONSTRAINT_FOREIGN
,
10444 fkconstraint
->deferrable
,
10445 fkconstraint
->initdeferred
,
10446 true, /* Is Enforced */
10447 fkconstraint
->initially_valid
,
10449 RelationGetRelid(rel
),
10453 InvalidOid
, /* not a domain constraint */
10455 RelationGetRelid(pkrel
),
10461 fkconstraint
->fk_upd_action
,
10462 fkconstraint
->fk_del_action
,
10465 fkconstraint
->fk_matchtype
,
10466 NULL
, /* no exclusion constraint */
10467 NULL
, /* no check constraint */
10469 conislocal
, /* islocal */
10470 coninhcount
, /* inhcount */
10471 connoinherit
, /* conNoInherit */
10472 with_period
, /* conPeriod */
10473 is_internal
); /* is_internal */
10475 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10478 * In partitioning cases, create the dependency entries for this
10479 * constraint. (For non-partitioned cases, relevant entries were created
10480 * by CreateConstraintEntry.)
10482 * On the referenced side, we need the constraint to have an internal
10483 * dependency on its parent constraint; this means that this constraint
10484 * cannot be dropped on its own -- only through the parent constraint. It
10485 * also means the containing partition cannot be dropped on its own, but
10486 * it can be detached, at which point this dependency is removed (after
10487 * verifying that no rows are referenced via this FK.)
10489 * When processing the referencing side, we link the constraint via the
10490 * special partitioning dependencies: the parent constraint is the primary
10491 * dependent, and the partition on which the foreign key exists is the
10492 * secondary dependency. That way, this constraint is dropped if either
10493 * of these objects is.
10495 * Note that this is only necessary for the subsidiary pg_constraint rows
10496 * in partitions; the topmost row doesn't need any of this.
10498 if (OidIsValid(parentConstr
))
10500 ObjectAddress referenced
;
10502 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10504 Assert(fkside
!= addFkBothSides
);
10505 if (fkside
== addFkReferencedSide
)
10506 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
10509 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10510 ObjectAddressSet(referenced
, RelationRelationId
, RelationGetRelid(rel
));
10511 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10515 /* make new constraint visible, in case we add more */
10516 CommandCounterIncrement();
10522 * addFkRecurseReferenced
10523 * Recursive helper for the referenced side of foreign key creation,
10524 * which creates the action triggers and recurses
10526 * If the referenced relation is a plain relation, create the necessary action
10527 * triggers that implement the constraint. If the referenced relation is a
10528 * partitioned table, then we create a pg_constraint row referencing the parent
10529 * of the referencing side for it and recurse on this routine for each
10532 * fkconstraint: the constraint being added
10533 * rel: the root referencing relation
10534 * pkrel: the referenced relation; might be a partition, if recursing
10535 * indexOid: the OID of the index (on pkrel) implementing this constraint
10536 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10537 * top-level constraint
10538 * numfks: the number of columns in the foreign key
10539 * pkattnum: the attnum array of referenced attributes
10540 * fkattnum: the attnum array of referencing attributes
10541 * numfkdelsetcols: the number of columns in the ON DELETE SET
10542 * NULL/DEFAULT (...) clause
10543 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10544 * NULL/DEFAULT clause
10545 * pf/pp/ffeqoperators: OID array of operators between columns
10546 * old_check_ok: true if this constraint replaces an existing one that
10547 * was already validated (thus this one doesn't need validation)
10548 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10549 * partition, the OIDs of the parent action triggers for DELETE and
10550 * UPDATE respectively.
10551 * with_period: true if this is a temporal FK
10554 addFkRecurseReferenced(Constraint
*fkconstraint
, Relation rel
,
10555 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10557 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
10558 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10559 int numfkdelsetcols
, int16
*fkdelsetcols
,
10561 Oid parentDelTrigger
, Oid parentUpdTrigger
,
10564 Oid deleteTriggerOid
,
10567 Assert(CheckRelationLockedByMe(pkrel
, ShareRowExclusiveLock
, true));
10568 Assert(CheckRelationLockedByMe(rel
, ShareRowExclusiveLock
, true));
10571 * Create the action triggers that enforce the constraint.
10573 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
10575 parentConstr
, indexOid
,
10576 parentDelTrigger
, parentUpdTrigger
,
10577 &deleteTriggerOid
, &updateTriggerOid
);
10580 * If the referenced table is partitioned, recurse on ourselves to handle
10581 * each partition. We need one pg_constraint row created for each
10582 * partition in addition to the pg_constraint row for the parent table.
10584 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10586 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
10588 for (int i
= 0; i
< pd
->nparts
; i
++)
10592 AttrNumber
*mapped_pkattnum
;
10594 ObjectAddress address
;
10596 /* XXX would it be better to acquire these locks beforehand? */
10597 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
10600 * Map the attribute numbers in the referenced side of the FK
10601 * definition to match the partition's column layout.
10603 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
10604 RelationGetDescr(pkrel
),
10608 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
10609 for (int j
= 0; j
< numfks
; j
++)
10610 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
10613 mapped_pkattnum
= pkattnum
;
10615 /* Determine the index to use at this level */
10616 partIndexId
= index_get_partition(partRel
, indexOid
);
10617 if (!OidIsValid(partIndexId
))
10618 elog(ERROR
, "index for %u not found in partition %s",
10619 indexOid
, RelationGetRelationName(partRel
));
10621 /* Create entry at this level ... */
10622 address
= addFkConstraint(addFkReferencedSide
,
10623 fkconstraint
->conname
, fkconstraint
, rel
,
10624 partRel
, partIndexId
, parentConstr
,
10625 numfks
, mapped_pkattnum
,
10626 fkattnum
, pfeqoperators
, ppeqoperators
,
10627 ffeqoperators
, numfkdelsetcols
,
10628 fkdelsetcols
, true, with_period
);
10629 /* ... and recurse to our children */
10630 addFkRecurseReferenced(fkconstraint
, rel
, partRel
,
10631 partIndexId
, address
.objectId
, numfks
,
10632 mapped_pkattnum
, fkattnum
,
10633 pfeqoperators
, ppeqoperators
, ffeqoperators
,
10634 numfkdelsetcols
, fkdelsetcols
,
10636 deleteTriggerOid
, updateTriggerOid
,
10639 /* Done -- clean up (but keep the lock) */
10640 table_close(partRel
, NoLock
);
10643 pfree(mapped_pkattnum
);
10651 * addFkRecurseReferencing
10652 * Recursive helper for the referencing side of foreign key creation,
10653 * which creates the check triggers and recurses
10655 * If the referencing relation is a plain relation, create the necessary check
10656 * triggers that implement the constraint, and set up for Phase 3 constraint
10657 * verification. If the referencing relation is a partitioned table, then
10658 * we create a pg_constraint row for it and recurse on this routine for each
10661 * We assume that the referenced relation is locked against concurrent
10662 * deletions. If it's a partitioned relation, every partition must be so
10665 * wqueue: the ALTER TABLE work queue; NULL when not running as part
10666 * of an ALTER TABLE sequence.
10667 * fkconstraint: the constraint being added
10668 * rel: the referencing relation; might be a partition, if recursing
10669 * pkrel: the root referenced relation
10670 * indexOid: the OID of the index (on pkrel) implementing this constraint
10671 * parentConstr: the OID of the parent constraint (there is always one)
10672 * numfks: the number of columns in the foreign key
10673 * pkattnum: the attnum array of referenced attributes
10674 * fkattnum: the attnum array of referencing attributes
10675 * pf/pp/ffeqoperators: OID array of operators between columns
10676 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10678 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10679 * NULL/DEFAULT clause
10680 * old_check_ok: true if this constraint replaces an existing one that
10681 * was already validated (thus this one doesn't need validation)
10682 * lockmode: the lockmode to acquire on partitions when recursing
10683 * parentInsTrigger and parentUpdTrigger: when being recursively called on
10684 * a partition, the OIDs of the parent check triggers for INSERT and
10685 * UPDATE respectively.
10686 * with_period: true if this is a temporal FK
10689 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10690 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10691 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
10692 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10693 int numfkdelsetcols
, int16
*fkdelsetcols
,
10694 bool old_check_ok
, LOCKMODE lockmode
,
10695 Oid parentInsTrigger
, Oid parentUpdTrigger
,
10698 Oid insertTriggerOid
,
10701 Assert(OidIsValid(parentConstr
));
10702 Assert(CheckRelationLockedByMe(rel
, ShareRowExclusiveLock
, true));
10703 Assert(CheckRelationLockedByMe(pkrel
, ShareRowExclusiveLock
, true));
10705 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10707 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10708 errmsg("foreign key constraints are not supported on foreign tables")));
10711 * Add the check triggers to it and, if necessary, schedule it to be
10712 * checked in Phase 3.
10714 * If the relation is partitioned, drill down to do it to its partitions.
10716 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
10717 RelationGetRelid(pkrel
),
10721 parentInsTrigger
, parentUpdTrigger
,
10722 &insertTriggerOid
, &updateTriggerOid
);
10724 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
10727 * Tell Phase 3 to check that the constraint is satisfied by existing
10728 * rows. We can skip this during table creation, when requested
10729 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10730 * and when we're recreating a constraint following a SET DATA TYPE
10731 * operation that did not impugn its validity.
10733 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
10735 NewConstraint
*newcon
;
10736 AlteredTableInfo
*tab
;
10738 tab
= ATGetQueueEntry(wqueue
, rel
);
10740 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
10741 newcon
->name
= get_constraint_name(parentConstr
);
10742 newcon
->contype
= CONSTR_FOREIGN
;
10743 newcon
->refrelid
= RelationGetRelid(pkrel
);
10744 newcon
->refindid
= indexOid
;
10745 newcon
->conid
= parentConstr
;
10746 newcon
->conwithperiod
= fkconstraint
->fk_with_period
;
10747 newcon
->qual
= (Node
*) fkconstraint
;
10749 tab
->constraints
= lappend(tab
->constraints
, newcon
);
10752 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10754 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
10758 * Triggers of the foreign keys will be manipulated a bunch of times
10759 * in the loop below. To avoid repeatedly opening/closing the trigger
10760 * catalog relation, we open it here and pass it to the subroutines
10763 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10766 * Recurse to take appropriate action on each partition; either we
10767 * find an existing constraint to reparent to ours, or we create a new
10770 for (int i
= 0; i
< pd
->nparts
; i
++)
10772 Oid partitionId
= pd
->oids
[i
];
10773 Relation partition
= table_open(partitionId
, lockmode
);
10776 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
10778 ObjectAddress address
;
10781 CheckAlterTableIsSafe(partition
);
10783 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
10784 RelationGetDescr(rel
),
10786 for (int j
= 0; j
< numfks
; j
++)
10787 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
10789 /* Check whether an existing constraint can be repurposed */
10790 partFKs
= copyObject(RelationGetFKeyList(partition
));
10792 foreach(cell
, partFKs
)
10794 ForeignKeyCacheInfo
*fk
;
10796 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
10797 if (tryAttachPartitionForeignKey(fk
,
10814 table_close(partition
, NoLock
);
10819 * No luck finding a good constraint to reuse; create our own.
10821 address
= addFkConstraint(addFkReferencingSide
,
10822 fkconstraint
->conname
, fkconstraint
,
10823 partition
, pkrel
, indexOid
, parentConstr
,
10825 mapped_fkattnum
, pfeqoperators
,
10826 ppeqoperators
, ffeqoperators
,
10827 numfkdelsetcols
, fkdelsetcols
, true,
10830 /* call ourselves to finalize the creation and we're done */
10831 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10848 table_close(partition
, NoLock
);
10851 table_close(trigrel
, RowExclusiveLock
);
10856 * CloneForeignKeyConstraints
10857 * Clone foreign keys from a partitioned table to a newly acquired
10860 * partitionRel is a partition of parentRel, so we can be certain that it has
10861 * the same columns with the same datatypes. The columns may be in different
10864 * wqueue must be passed to set up phase 3 constraint checking, unless the
10865 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10869 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10870 Relation partitionRel
)
10872 /* This only works for declarative partitioning */
10873 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10876 * Clone constraints for which the parent is on the referenced side.
10878 CloneFkReferenced(parentRel
, partitionRel
);
10881 * Now clone constraints where the parent is on the referencing side.
10883 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10887 * CloneFkReferenced
10888 * Subroutine for CloneForeignKeyConstraints
10890 * Find all the FKs that have the parent relation on the referenced side;
10891 * clone those constraints to the given partition. This is to be called
10892 * when the partition is being created or attached.
10894 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10896 * This recurses to partitions, if the relation being attached is partitioned.
10897 * Recursion is done by calling addFkRecurseReferenced.
10900 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10902 Relation pg_constraint
;
10906 ScanKeyData key
[2];
10912 * Search for any constraints where this partition's parent is in the
10913 * referenced side. However, we must not clone any constraint whose
10914 * parent constraint is also going to be cloned, to avoid duplicates. So
10915 * do it in two steps: first construct the list of constraints to clone,
10916 * then go over that list cloning those whose parents are not in the list.
10917 * (We must not rely on the parent being seen first, since the catalog
10918 * scan could return children first.)
10920 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
10921 ScanKeyInit(&key
[0],
10922 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
10923 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
10924 ScanKeyInit(&key
[1],
10925 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
10926 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
10927 /* This is a seqscan, as we don't have a usable index ... */
10928 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
10930 while ((tuple
= systable_getnext(scan
)) != NULL
)
10932 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10934 clone
= lappend_oid(clone
, constrForm
->oid
);
10936 systable_endscan(scan
);
10937 table_close(pg_constraint
, RowShareLock
);
10940 * Triggers of the foreign keys will be manipulated a bunch of times in
10941 * the loop below. To avoid repeatedly opening/closing the trigger
10942 * catalog relation, we open it here and pass it to the subroutines called
10945 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10947 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
10948 RelationGetDescr(parentRel
),
10950 foreach(cell
, clone
)
10952 Oid constrOid
= lfirst_oid(cell
);
10953 Form_pg_constraint constrForm
;
10958 AttrNumber conkey
[INDEX_MAX_KEYS
];
10959 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
10960 AttrNumber confkey
[INDEX_MAX_KEYS
];
10961 Oid conpfeqop
[INDEX_MAX_KEYS
];
10962 Oid conppeqop
[INDEX_MAX_KEYS
];
10963 Oid conffeqop
[INDEX_MAX_KEYS
];
10964 int numfkdelsetcols
;
10965 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10966 Constraint
*fkconstraint
;
10967 ObjectAddress address
;
10968 Oid deleteTriggerOid
,
10971 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
10972 if (!HeapTupleIsValid(tuple
))
10973 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
10974 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10977 * As explained above: don't try to clone a constraint for which we're
10978 * going to clone the parent.
10980 if (list_member_oid(clone
, constrForm
->conparentid
))
10982 ReleaseSysCache(tuple
);
10987 * Don't clone self-referencing foreign keys, which can be in the
10988 * partitioned table or in the partition-to-be.
10990 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
10991 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
10993 ReleaseSysCache(tuple
);
10997 /* We need the same lock level that CreateTrigger will acquire */
10998 fkRel
= table_open(constrForm
->conrelid
, ShareRowExclusiveLock
);
11000 indexOid
= constrForm
->conindid
;
11001 DeconstructFkConstraintRow(tuple
,
11011 for (int i
= 0; i
< numfks
; i
++)
11012 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
11014 fkconstraint
= makeNode(Constraint
);
11015 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11016 fkconstraint
->conname
= NameStr(constrForm
->conname
);
11017 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11018 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11019 fkconstraint
->location
= -1;
11020 fkconstraint
->pktable
= NULL
;
11021 /* ->fk_attrs determined below */
11022 fkconstraint
->pk_attrs
= NIL
;
11023 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11024 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11025 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11026 fkconstraint
->fk_del_set_cols
= NIL
;
11027 fkconstraint
->old_conpfeqop
= NIL
;
11028 fkconstraint
->old_pktable_oid
= InvalidOid
;
11029 fkconstraint
->skip_validation
= false;
11030 fkconstraint
->initially_valid
= true;
11032 /* set up colnames that are used to generate the constraint name */
11033 for (int i
= 0; i
< numfks
; i
++)
11035 Form_pg_attribute att
;
11037 att
= TupleDescAttr(RelationGetDescr(fkRel
),
11039 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11040 makeString(NameStr(att
->attname
)));
11044 * Add the new foreign key constraint pointing to the new partition.
11045 * Because this new partition appears in the referenced side of the
11046 * constraint, we don't need to set up for Phase 3 check.
11048 partIndexId
= index_get_partition(partitionRel
, indexOid
);
11049 if (!OidIsValid(partIndexId
))
11050 elog(ERROR
, "index for %u not found in partition %s",
11051 indexOid
, RelationGetRelationName(partitionRel
));
11054 * Get the "action" triggers belonging to the constraint to pass as
11055 * parent OIDs for similar triggers that will be created on the
11056 * partition in addFkRecurseReferenced().
11058 GetForeignKeyActionTriggers(trigrel
, constrOid
,
11059 constrForm
->confrelid
, constrForm
->conrelid
,
11060 &deleteTriggerOid
, &updateTriggerOid
);
11062 /* Add this constraint ... */
11063 address
= addFkConstraint(addFkReferencedSide
,
11064 fkconstraint
->conname
, fkconstraint
, fkRel
,
11065 partitionRel
, partIndexId
, constrOid
,
11066 numfks
, mapped_confkey
,
11067 conkey
, conpfeqop
, conppeqop
, conffeqop
,
11068 numfkdelsetcols
, confdelsetcols
, false,
11069 constrForm
->conperiod
);
11070 /* ... and recurse */
11071 addFkRecurseReferenced(fkconstraint
,
11087 constrForm
->conperiod
);
11089 table_close(fkRel
, NoLock
);
11090 ReleaseSysCache(tuple
);
11093 table_close(trigrel
, RowExclusiveLock
);
11097 * CloneFkReferencing
11098 * Subroutine for CloneForeignKeyConstraints
11100 * For each FK constraint of the parent relation in the given list, find an
11101 * equivalent constraint in its partition relation that can be reparented;
11102 * if one cannot be found, create a new constraint in the partition as its
11105 * If wqueue is given, it is used to set up phase-3 verification for each
11106 * cloned constraint; omit it if such verification is not needed
11107 * (example: the partition is being created anew).
11110 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
11118 /* obtain a list of constraints that we need to clone */
11119 foreach(cell
, RelationGetFKeyList(parentRel
))
11121 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
11124 * Refuse to attach a table as partition that this partitioned table
11125 * already has a foreign key to. This isn't useful schema, which is
11126 * proven by the fact that there have been no user complaints that
11127 * it's already impossible to achieve this in the opposite direction,
11128 * i.e., creating a foreign key that references a partition. This
11129 * restriction allows us to dodge some complexities around
11130 * pg_constraint and pg_trigger row creations that would be needed
11131 * during ATTACH/DETACH for this kind of relationship.
11133 if (fk
->confrelid
== RelationGetRelid(partRel
))
11135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
11136 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11137 RelationGetRelationName(partRel
),
11138 get_constraint_name(fk
->conoid
))));
11140 clone
= lappend_oid(clone
, fk
->conoid
);
11144 * Silently do nothing if there's nothing to do. In particular, this
11145 * avoids throwing a spurious error for foreign tables.
11150 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
11152 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11153 errmsg("foreign key constraints are not supported on foreign tables")));
11156 * Triggers of the foreign keys will be manipulated a bunch of times in
11157 * the loop below. To avoid repeatedly opening/closing the trigger
11158 * catalog relation, we open it here and pass it to the subroutines called
11161 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11164 * The constraint key may differ, if the columns in the partition are
11165 * different. This map is used to convert them.
11167 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
11168 RelationGetDescr(parentRel
),
11171 partFKs
= copyObject(RelationGetFKeyList(partRel
));
11173 foreach(cell
, clone
)
11175 Oid parentConstrOid
= lfirst_oid(cell
);
11176 Form_pg_constraint constrForm
;
11180 AttrNumber conkey
[INDEX_MAX_KEYS
];
11181 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
11182 AttrNumber confkey
[INDEX_MAX_KEYS
];
11183 Oid conpfeqop
[INDEX_MAX_KEYS
];
11184 Oid conppeqop
[INDEX_MAX_KEYS
];
11185 Oid conffeqop
[INDEX_MAX_KEYS
];
11186 int numfkdelsetcols
;
11187 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
11188 Constraint
*fkconstraint
;
11191 ObjectAddress address
;
11193 Oid insertTriggerOid
,
11197 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
11198 if (!HeapTupleIsValid(tuple
))
11199 elog(ERROR
, "cache lookup failed for constraint %u",
11201 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11203 /* Don't clone constraints whose parents are being cloned */
11204 if (list_member_oid(clone
, constrForm
->conparentid
))
11206 ReleaseSysCache(tuple
);
11211 * Need to prevent concurrent deletions. If pkrel is a partitioned
11212 * relation, that means to lock all partitions.
11214 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
11215 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11216 (void) find_all_inheritors(RelationGetRelid(pkrel
),
11217 ShareRowExclusiveLock
, NULL
);
11219 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
11220 conpfeqop
, conppeqop
, conffeqop
,
11221 &numfkdelsetcols
, confdelsetcols
);
11222 for (int i
= 0; i
< numfks
; i
++)
11223 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
11226 * Get the "check" triggers belonging to the constraint to pass as
11227 * parent OIDs for similar triggers that will be created on the
11228 * partition in addFkRecurseReferencing(). They are also passed to
11229 * tryAttachPartitionForeignKey() below to simply assign as parents to
11230 * the partition's existing "check" triggers, that is, if the
11231 * corresponding constraints is deemed attachable to the parent
11234 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
11235 constrForm
->confrelid
, constrForm
->conrelid
,
11236 &insertTriggerOid
, &updateTriggerOid
);
11239 * Before creating a new constraint, see whether any existing FKs are
11240 * fit for the purpose. If one is, attach the parent constraint to
11241 * it, and don't clone anything. This way we avoid the expensive
11242 * verification step and don't end up with a duplicate FK, and we
11243 * don't need to recurse to partitions for this constraint.
11246 foreach(lc
, partFKs
)
11248 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
11250 if (tryAttachPartitionForeignKey(fk
,
11251 RelationGetRelid(partRel
),
11262 table_close(pkrel
, NoLock
);
11268 ReleaseSysCache(tuple
);
11272 /* No dice. Set up to create our own constraint */
11273 fkconstraint
= makeNode(Constraint
);
11274 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11275 /* ->conname determined below */
11276 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11277 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11278 fkconstraint
->location
= -1;
11279 fkconstraint
->pktable
= NULL
;
11280 /* ->fk_attrs determined below */
11281 fkconstraint
->pk_attrs
= NIL
;
11282 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11283 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11284 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11285 fkconstraint
->fk_del_set_cols
= NIL
;
11286 fkconstraint
->old_conpfeqop
= NIL
;
11287 fkconstraint
->old_pktable_oid
= InvalidOid
;
11288 fkconstraint
->skip_validation
= false;
11289 fkconstraint
->initially_valid
= constrForm
->convalidated
;
11290 for (int i
= 0; i
< numfks
; i
++)
11292 Form_pg_attribute att
;
11294 att
= TupleDescAttr(RelationGetDescr(partRel
),
11295 mapped_conkey
[i
] - 1);
11296 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11297 makeString(NameStr(att
->attname
)));
11300 indexOid
= constrForm
->conindid
;
11301 with_period
= constrForm
->conperiod
;
11303 /* Create the pg_constraint entry at this level */
11304 address
= addFkConstraint(addFkReferencingSide
,
11305 NameStr(constrForm
->conname
), fkconstraint
,
11306 partRel
, pkrel
, indexOid
, parentConstrOid
,
11308 mapped_conkey
, conpfeqop
,
11309 conppeqop
, conffeqop
,
11310 numfkdelsetcols
, confdelsetcols
,
11311 false, with_period
);
11313 /* Done with the cloned constraint's tuple */
11314 ReleaseSysCache(tuple
);
11316 /* Create the check triggers, and recurse to partitions, if any */
11317 addFkRecurseReferencing(wqueue
,
11331 false, /* no old check exists */
11332 AccessExclusiveLock
,
11336 table_close(pkrel
, NoLock
);
11339 table_close(trigrel
, RowExclusiveLock
);
11343 * When the parent of a partition receives [the referencing side of] a foreign
11344 * key, we must propagate that foreign key to the partition. However, the
11345 * partition might already have an equivalent foreign key; this routine
11346 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11347 * by the other parameters. If they are equivalent, create the link between
11348 * the two constraints and return true.
11350 * If the given FK does not match the one defined by rest of the params,
11354 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
11356 Oid parentConstrOid
,
11358 AttrNumber
*mapped_conkey
,
11359 AttrNumber
*confkey
,
11361 Oid parentInsTrigger
,
11362 Oid parentUpdTrigger
,
11365 HeapTuple parentConstrTup
;
11366 Form_pg_constraint parentConstr
;
11367 HeapTuple partcontup
;
11368 Form_pg_constraint partConstr
;
11372 Oid insertTriggerOid
,
11375 parentConstrTup
= SearchSysCache1(CONSTROID
,
11376 ObjectIdGetDatum(parentConstrOid
));
11377 if (!HeapTupleIsValid(parentConstrTup
))
11378 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
11379 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
11382 * Do some quick & easy initial checks. If any of these fail, we cannot
11383 * use this constraint.
11385 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
11387 ReleaseSysCache(parentConstrTup
);
11390 for (int i
= 0; i
< numfks
; i
++)
11392 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
11393 fk
->confkey
[i
] != confkey
[i
] ||
11394 fk
->conpfeqop
[i
] != conpfeqop
[i
])
11396 ReleaseSysCache(parentConstrTup
);
11402 * Looks good so far; do some more extensive checks. Presumably the check
11403 * for 'convalidated' could be dropped, since we don't really care about
11404 * that, but let's be careful for now.
11406 partcontup
= SearchSysCache1(CONSTROID
,
11407 ObjectIdGetDatum(fk
->conoid
));
11408 if (!HeapTupleIsValid(partcontup
))
11409 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
11410 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
11411 if (OidIsValid(partConstr
->conparentid
) ||
11412 !partConstr
->convalidated
||
11413 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
11414 partConstr
->condeferred
!= parentConstr
->condeferred
||
11415 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
11416 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
11417 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
11419 ReleaseSysCache(parentConstrTup
);
11420 ReleaseSysCache(partcontup
);
11424 ReleaseSysCache(partcontup
);
11425 ReleaseSysCache(parentConstrTup
);
11428 * Looks good! Attach this constraint. The action triggers in the new
11429 * partition become redundant -- the parent table already has equivalent
11430 * ones, and those will be able to reach the partition. Remove the ones
11431 * in the partition. We identify them because they have our constraint
11432 * OID, as well as being on the referenced rel.
11435 Anum_pg_trigger_tgconstraint
,
11436 BTEqualStrategyNumber
, F_OIDEQ
,
11437 ObjectIdGetDatum(fk
->conoid
));
11438 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11440 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11442 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11443 ObjectAddress trigger
;
11445 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
11447 if (trgform
->tgrelid
!= fk
->confrelid
)
11451 * The constraint is originally set up to contain this trigger as an
11452 * implementation object, so there's a dependency record that links
11453 * the two; however, since the trigger is no longer needed, we remove
11454 * the dependency link in order to be able to drop the trigger while
11455 * keeping the constraint intact.
11457 deleteDependencyRecordsFor(TriggerRelationId
,
11460 /* make dependency deletion visible to performDeletion */
11461 CommandCounterIncrement();
11462 ObjectAddressSet(trigger
, TriggerRelationId
,
11464 performDeletion(&trigger
, DROP_RESTRICT
, 0);
11465 /* make trigger drop visible, in case the loop iterates */
11466 CommandCounterIncrement();
11469 systable_endscan(scan
);
11471 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
11474 * Like the constraint, attach partition's "check" triggers to the
11475 * corresponding parent triggers.
11477 GetForeignKeyCheckTriggers(trigrel
,
11478 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
11479 &insertTriggerOid
, &updateTriggerOid
);
11480 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
11481 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
11483 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
11484 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
11488 * If the referenced table is partitioned, then the partition we're
11489 * attaching now has extra pg_constraint rows and action triggers that are
11490 * no longer needed. Remove those.
11492 if (get_rel_relkind(fk
->confrelid
) == RELKIND_PARTITIONED_TABLE
)
11494 Relation pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
11495 ObjectAddresses
*objs
;
11496 HeapTuple consttup
;
11499 Anum_pg_constraint_conrelid
,
11500 BTEqualStrategyNumber
, F_OIDEQ
,
11501 ObjectIdGetDatum(fk
->conrelid
));
11503 scan
= systable_beginscan(pg_constraint
,
11504 ConstraintRelidTypidNameIndexId
,
11505 true, NULL
, 1, &key
);
11506 objs
= new_object_addresses();
11507 while ((consttup
= systable_getnext(scan
)) != NULL
)
11509 Form_pg_constraint conform
= (Form_pg_constraint
) GETSTRUCT(consttup
);
11511 if (conform
->conparentid
!= fk
->conoid
)
11515 ObjectAddress addr
;
11518 int n PG_USED_FOR_ASSERTS_ONLY
;
11520 ObjectAddressSet(addr
, ConstraintRelationId
, conform
->oid
);
11521 add_exact_object_address(&addr
, objs
);
11524 * First we must delete the dependency record that binds the
11525 * constraint records together.
11527 n
= deleteDependencyRecordsForSpecific(ConstraintRelationId
,
11529 DEPENDENCY_INTERNAL
,
11530 ConstraintRelationId
,
11532 Assert(n
== 1); /* actually only one is expected */
11535 * Now search for the triggers for this constraint and set
11536 * them up for deletion too
11539 Anum_pg_trigger_tgconstraint
,
11540 BTEqualStrategyNumber
, F_OIDEQ
,
11541 ObjectIdGetDatum(conform
->oid
));
11542 scan2
= systable_beginscan(trigrel
, TriggerConstraintIndexId
,
11543 true, NULL
, 1, &key2
);
11544 while ((trigtup
= systable_getnext(scan2
)) != NULL
)
11546 ObjectAddressSet(addr
, TriggerRelationId
,
11547 ((Form_pg_trigger
) GETSTRUCT(trigtup
))->oid
);
11548 add_exact_object_address(&addr
, objs
);
11550 systable_endscan(scan2
);
11553 /* make the dependency deletions visible */
11554 CommandCounterIncrement();
11555 performMultipleDeletions(objs
, DROP_RESTRICT
,
11556 PERFORM_DELETION_INTERNAL
);
11557 systable_endscan(scan
);
11559 table_close(pg_constraint
, RowShareLock
);
11562 CommandCounterIncrement();
11567 * GetForeignKeyActionTriggers
11568 * Returns delete and update "action" triggers of the given relation
11569 * belonging to the given constraint
11572 GetForeignKeyActionTriggers(Relation trigrel
,
11573 Oid conoid
, Oid confrelid
, Oid conrelid
,
11574 Oid
*deleteTriggerOid
,
11575 Oid
*updateTriggerOid
)
11581 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
11583 Anum_pg_trigger_tgconstraint
,
11584 BTEqualStrategyNumber
, F_OIDEQ
,
11585 ObjectIdGetDatum(conoid
));
11587 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11589 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11591 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11593 if (trgform
->tgconstrrelid
!= conrelid
)
11595 if (trgform
->tgrelid
!= confrelid
)
11597 /* Only ever look at "action" triggers on the PK side. */
11598 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
11600 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
11602 Assert(*deleteTriggerOid
== InvalidOid
);
11603 *deleteTriggerOid
= trgform
->oid
;
11605 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11607 Assert(*updateTriggerOid
== InvalidOid
);
11608 *updateTriggerOid
= trgform
->oid
;
11610 #ifndef USE_ASSERT_CHECKING
11611 /* In an assert-enabled build, continue looking to find duplicates */
11612 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
11617 if (!OidIsValid(*deleteTriggerOid
))
11618 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
11620 if (!OidIsValid(*updateTriggerOid
))
11621 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
11624 systable_endscan(scan
);
11628 * GetForeignKeyCheckTriggers
11629 * Returns insert and update "check" triggers of the given relation
11630 * belonging to the given constraint
11633 GetForeignKeyCheckTriggers(Relation trigrel
,
11634 Oid conoid
, Oid confrelid
, Oid conrelid
,
11635 Oid
*insertTriggerOid
,
11636 Oid
*updateTriggerOid
)
11642 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
11644 Anum_pg_trigger_tgconstraint
,
11645 BTEqualStrategyNumber
, F_OIDEQ
,
11646 ObjectIdGetDatum(conoid
));
11648 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11650 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11652 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11654 if (trgform
->tgconstrrelid
!= confrelid
)
11656 if (trgform
->tgrelid
!= conrelid
)
11658 /* Only ever look at "check" triggers on the FK side. */
11659 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
11661 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
11663 Assert(*insertTriggerOid
== InvalidOid
);
11664 *insertTriggerOid
= trgform
->oid
;
11666 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11668 Assert(*updateTriggerOid
== InvalidOid
);
11669 *updateTriggerOid
= trgform
->oid
;
11671 #ifndef USE_ASSERT_CHECKING
11672 /* In an assert-enabled build, continue looking to find duplicates. */
11673 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
11678 if (!OidIsValid(*insertTriggerOid
))
11679 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
11681 if (!OidIsValid(*updateTriggerOid
))
11682 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
11685 systable_endscan(scan
);
11689 * ALTER TABLE ALTER CONSTRAINT
11691 * Update the attributes of a constraint.
11693 * Currently only works for Foreign Key constraints.
11695 * If the constraint is modified, returns its address; otherwise, return
11696 * InvalidObjectAddress.
11698 static ObjectAddress
11699 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
11700 bool recursing
, LOCKMODE lockmode
)
11702 Constraint
*cmdcon
;
11706 ScanKeyData skey
[3];
11707 HeapTuple contuple
;
11708 Form_pg_constraint currcon
;
11709 ObjectAddress address
;
11710 List
*otherrelids
= NIL
;
11713 cmdcon
= castNode(Constraint
, cmd
->def
);
11715 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11716 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11719 * Find and check the target constraint
11721 ScanKeyInit(&skey
[0],
11722 Anum_pg_constraint_conrelid
,
11723 BTEqualStrategyNumber
, F_OIDEQ
,
11724 ObjectIdGetDatum(RelationGetRelid(rel
)));
11725 ScanKeyInit(&skey
[1],
11726 Anum_pg_constraint_contypid
,
11727 BTEqualStrategyNumber
, F_OIDEQ
,
11728 ObjectIdGetDatum(InvalidOid
));
11729 ScanKeyInit(&skey
[2],
11730 Anum_pg_constraint_conname
,
11731 BTEqualStrategyNumber
, F_NAMEEQ
,
11732 CStringGetDatum(cmdcon
->conname
));
11733 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11734 true, NULL
, 3, skey
);
11736 /* There can be at most one matching row */
11737 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
11739 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11740 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11741 cmdcon
->conname
, RelationGetRelationName(rel
))));
11743 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11744 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
11746 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11747 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11748 cmdcon
->conname
, RelationGetRelationName(rel
))));
11751 * If it's not the topmost constraint, raise an error.
11753 * Altering a non-topmost constraint leaves some triggers untouched, since
11754 * they are not directly connected to this constraint; also, pg_dump would
11755 * ignore the deferrability status of the individual constraint, since it
11756 * only dumps topmost constraints. Avoid these problems by refusing this
11757 * operation and telling the user to alter the parent constraint instead.
11759 if (OidIsValid(currcon
->conparentid
))
11762 Oid parent
= currcon
->conparentid
;
11763 char *ancestorname
= NULL
;
11764 char *ancestortable
= NULL
;
11766 /* Loop to find the topmost constraint */
11767 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
11769 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
11771 /* If no parent, this is the constraint we want */
11772 if (!OidIsValid(contup
->conparentid
))
11774 ancestorname
= pstrdup(NameStr(contup
->conname
));
11775 ancestortable
= get_rel_name(contup
->conrelid
);
11776 ReleaseSysCache(tp
);
11780 parent
= contup
->conparentid
;
11781 ReleaseSysCache(tp
);
11785 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11786 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11787 cmdcon
->conname
, RelationGetRelationName(rel
)),
11788 ancestorname
&& ancestortable
?
11789 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11790 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
11791 errhint("You may alter the constraint it derives from instead.")));
11795 * Do the actual catalog work. We can skip changing if already in the
11796 * desired state, but not if a partitioned table: partitions need to be
11797 * processed regardless, in case they had the constraint locally changed.
11799 address
= InvalidObjectAddress
;
11800 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11801 currcon
->condeferred
!= cmdcon
->initdeferred
||
11802 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11804 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
11805 &otherrelids
, lockmode
))
11806 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
11810 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11811 * having the constraint itself; here we also invalidate for relations
11812 * that have any triggers that are part of the constraint.
11814 foreach(lc
, otherrelids
)
11815 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
11817 systable_endscan(scan
);
11819 table_close(tgrel
, RowExclusiveLock
);
11820 table_close(conrel
, RowExclusiveLock
);
11826 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11827 * constraint is altered.
11829 * *otherrelids is appended OIDs of relations containing affected triggers.
11831 * Note that we must recurse even when the values are correct, in case
11832 * indirect descendants have had their constraints altered locally.
11833 * (This could be avoided if we forbade altering constraints in partitions
11834 * but existing releases don't do that.)
11837 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
11838 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
11841 Form_pg_constraint currcon
;
11844 bool changed
= false;
11846 /* since this function recurses, it could be driven to stack overflow */
11847 check_stack_depth();
11849 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11850 conoid
= currcon
->oid
;
11851 refrelid
= currcon
->confrelid
;
11854 * Update pg_constraint with the flags from cmdcon.
11856 * If called to modify a constraint that's already in the desired state,
11857 * silently do nothing.
11859 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11860 currcon
->condeferred
!= cmdcon
->initdeferred
)
11862 HeapTuple copyTuple
;
11863 Form_pg_constraint copy_con
;
11866 SysScanDesc tgscan
;
11868 copyTuple
= heap_copytuple(contuple
);
11869 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11870 copy_con
->condeferrable
= cmdcon
->deferrable
;
11871 copy_con
->condeferred
= cmdcon
->initdeferred
;
11872 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11874 InvokeObjectPostAlterHook(ConstraintRelationId
,
11877 heap_freetuple(copyTuple
);
11880 /* Make new constraint flags visible to others */
11881 CacheInvalidateRelcache(rel
);
11884 * Now we need to update the multiple entries in pg_trigger that
11885 * implement the constraint.
11887 ScanKeyInit(&tgkey
,
11888 Anum_pg_trigger_tgconstraint
,
11889 BTEqualStrategyNumber
, F_OIDEQ
,
11890 ObjectIdGetDatum(conoid
));
11891 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11893 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11895 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11896 Form_pg_trigger copy_tg
;
11897 HeapTuple tgCopyTuple
;
11900 * Remember OIDs of other relation(s) involved in FK constraint.
11901 * (Note: it's likely that we could skip forcing a relcache inval
11902 * for other rels that don't have a trigger whose properties
11903 * change, but let's be conservative.)
11905 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11906 *otherrelids
= list_append_unique_oid(*otherrelids
,
11910 * Update deferrability of RI_FKey_noaction_del,
11911 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11912 * triggers, but not others; see createForeignKeyActionTriggers
11913 * and CreateFKCheckTrigger.
11915 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11916 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11917 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11918 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11921 tgCopyTuple
= heap_copytuple(tgtuple
);
11922 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11924 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11925 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11926 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11928 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11930 heap_freetuple(tgCopyTuple
);
11933 systable_endscan(tgscan
);
11937 * If the table at either end of the constraint is partitioned, we need to
11938 * recurse and handle every constraint that is a child of this one.
11940 * (This assumes that the recurse flag is forcibly set for partitioned
11941 * tables, and not set for legacy inheritance, though we don't check for
11944 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11945 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11949 HeapTuple childtup
;
11952 Anum_pg_constraint_conparentid
,
11953 BTEqualStrategyNumber
, F_OIDEQ
,
11954 ObjectIdGetDatum(conoid
));
11956 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11957 true, NULL
, 1, &pkey
);
11959 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11961 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11964 childrel
= table_open(childcon
->conrelid
, lockmode
);
11965 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11966 otherrelids
, lockmode
);
11967 table_close(childrel
, NoLock
);
11970 systable_endscan(pscan
);
11977 * ALTER TABLE VALIDATE CONSTRAINT
11979 * XXX The reason we handle recursion here rather than at Phase 1 is because
11980 * there's no good way to skip recursing when handling foreign keys: there is
11981 * no need to lock children in that case, yet we wouldn't be able to avoid
11982 * doing so at that level.
11984 * Return value is the address of the validated constraint. If the constraint
11985 * was already validated, InvalidObjectAddress is returned.
11987 static ObjectAddress
11988 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
11989 bool recurse
, bool recursing
, LOCKMODE lockmode
)
11993 ScanKeyData skey
[3];
11995 Form_pg_constraint con
;
11996 ObjectAddress address
;
11998 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12001 * Find and check the target constraint
12003 ScanKeyInit(&skey
[0],
12004 Anum_pg_constraint_conrelid
,
12005 BTEqualStrategyNumber
, F_OIDEQ
,
12006 ObjectIdGetDatum(RelationGetRelid(rel
)));
12007 ScanKeyInit(&skey
[1],
12008 Anum_pg_constraint_contypid
,
12009 BTEqualStrategyNumber
, F_OIDEQ
,
12010 ObjectIdGetDatum(InvalidOid
));
12011 ScanKeyInit(&skey
[2],
12012 Anum_pg_constraint_conname
,
12013 BTEqualStrategyNumber
, F_NAMEEQ
,
12014 CStringGetDatum(constrName
));
12015 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12016 true, NULL
, 3, skey
);
12018 /* There can be at most one matching row */
12019 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12021 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12022 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12023 constrName
, RelationGetRelationName(rel
))));
12025 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
12026 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
12027 con
->contype
!= CONSTRAINT_CHECK
)
12029 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12030 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12031 constrName
, RelationGetRelationName(rel
))));
12033 if (!con
->conenforced
)
12035 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12036 errmsg("cannot validate NOT ENFORCED constraint")));
12038 if (!con
->convalidated
)
12040 AlteredTableInfo
*tab
;
12041 HeapTuple copyTuple
;
12042 Form_pg_constraint copy_con
;
12044 if (con
->contype
== CONSTRAINT_FOREIGN
)
12046 NewConstraint
*newcon
;
12047 Constraint
*fkconstraint
;
12049 /* Queue validation for phase 3 */
12050 fkconstraint
= makeNode(Constraint
);
12051 /* for now this is all we need */
12052 fkconstraint
->conname
= constrName
;
12054 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
12055 newcon
->name
= constrName
;
12056 newcon
->contype
= CONSTR_FOREIGN
;
12057 newcon
->refrelid
= con
->confrelid
;
12058 newcon
->refindid
= con
->conindid
;
12059 newcon
->conid
= con
->oid
;
12060 newcon
->qual
= (Node
*) fkconstraint
;
12062 /* Find or create work queue entry for this table */
12063 tab
= ATGetQueueEntry(wqueue
, rel
);
12064 tab
->constraints
= lappend(tab
->constraints
, newcon
);
12067 * We disallow creating invalid foreign keys to or from
12068 * partitioned tables, so ignoring the recursion bit is okay.
12071 else if (con
->contype
== CONSTRAINT_CHECK
)
12073 List
*children
= NIL
;
12075 NewConstraint
*newcon
;
12080 * If we're recursing, the parent has already done this, so skip
12081 * it. Also, if the constraint is a NO INHERIT constraint, we
12082 * shouldn't try to look for it in the children.
12084 if (!recursing
&& !con
->connoinherit
)
12085 children
= find_all_inheritors(RelationGetRelid(rel
),
12089 * For CHECK constraints, we must ensure that we only mark the
12090 * constraint as validated on the parent if it's already validated
12093 * We recurse before validating on the parent, to reduce risk of
12096 foreach(child
, children
)
12098 Oid childoid
= lfirst_oid(child
);
12101 if (childoid
== RelationGetRelid(rel
))
12105 * If we are told not to recurse, there had better not be any
12106 * child tables, because we can't mark the constraint on the
12107 * parent valid unless it is valid for all child tables.
12111 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12112 errmsg("constraint must be validated on child tables too")));
12114 /* find_all_inheritors already got lock */
12115 childrel
= table_open(childoid
, NoLock
);
12117 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
12119 table_close(childrel
, NoLock
);
12122 /* Queue validation for phase 3 */
12123 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
12124 newcon
->name
= constrName
;
12125 newcon
->contype
= CONSTR_CHECK
;
12126 newcon
->refrelid
= InvalidOid
;
12127 newcon
->refindid
= InvalidOid
;
12128 newcon
->conid
= con
->oid
;
12130 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
12131 Anum_pg_constraint_conbin
);
12132 conbin
= TextDatumGetCString(val
);
12133 newcon
->qual
= (Node
*) stringToNode(conbin
);
12135 /* Find or create work queue entry for this table */
12136 tab
= ATGetQueueEntry(wqueue
, rel
);
12137 tab
->constraints
= lappend(tab
->constraints
, newcon
);
12140 * Invalidate relcache so that others see the new validated
12143 CacheInvalidateRelcache(rel
);
12147 * Now update the catalog, while we have the door open.
12149 copyTuple
= heap_copytuple(tuple
);
12150 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
12151 copy_con
->convalidated
= true;
12152 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
12154 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
12156 heap_freetuple(copyTuple
);
12158 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
12161 address
= InvalidObjectAddress
; /* already validated */
12163 systable_endscan(scan
);
12165 table_close(conrel
, RowExclusiveLock
);
12172 * transformColumnNameList - transform list of column names
12174 * Lookup each name and return its attnum and, optionally, type and collation
12177 * Note: the name of this function suggests that it's general-purpose,
12178 * but actually it's only used to look up names appearing in foreign-key
12179 * clauses. The error messages would need work to use it in other cases,
12180 * and perhaps the validity checks as well.
12183 transformColumnNameList(Oid relId
, List
*colList
,
12184 int16
*attnums
, Oid
*atttypids
, Oid
*attcollids
)
12190 foreach(l
, colList
)
12192 char *attname
= strVal(lfirst(l
));
12193 HeapTuple atttuple
;
12194 Form_pg_attribute attform
;
12196 atttuple
= SearchSysCacheAttName(relId
, attname
);
12197 if (!HeapTupleIsValid(atttuple
))
12199 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12200 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12202 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
12203 if (attform
->attnum
< 0)
12205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12206 errmsg("system columns cannot be used in foreign keys")));
12207 if (attnum
>= INDEX_MAX_KEYS
)
12209 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
12210 errmsg("cannot have more than %d keys in a foreign key",
12212 attnums
[attnum
] = attform
->attnum
;
12213 if (atttypids
!= NULL
)
12214 atttypids
[attnum
] = attform
->atttypid
;
12215 if (attcollids
!= NULL
)
12216 attcollids
[attnum
] = attform
->attcollation
;
12217 ReleaseSysCache(atttuple
);
12225 * transformFkeyGetPrimaryKey -
12227 * Look up the names, attnums, types, and collations of the primary key attributes
12228 * for the pkrel. Also return the index OID and index opclasses of the
12229 * index supporting the primary key. Also return whether the index has
12230 * WITHOUT OVERLAPS.
12232 * All parameters except pkrel are output parameters. Also, the function
12233 * return value is the number of attributes in the primary key.
12235 * Used when the column list in the REFERENCES specification is omitted.
12238 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
12239 List
**attnamelist
,
12240 int16
*attnums
, Oid
*atttypids
, Oid
*attcollids
,
12241 Oid
*opclasses
, bool *pk_has_without_overlaps
)
12243 List
*indexoidlist
;
12244 ListCell
*indexoidscan
;
12245 HeapTuple indexTuple
= NULL
;
12246 Form_pg_index indexStruct
= NULL
;
12247 Datum indclassDatum
;
12248 oidvector
*indclass
;
12252 * Get the list of index OIDs for the table from the relcache, and look up
12253 * each one in the pg_index syscache until we find one marked primary key
12254 * (hopefully there isn't more than one such). Insist it's valid, too.
12256 *indexOid
= InvalidOid
;
12258 indexoidlist
= RelationGetIndexList(pkrel
);
12260 foreach(indexoidscan
, indexoidlist
)
12262 Oid indexoid
= lfirst_oid(indexoidscan
);
12264 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12265 if (!HeapTupleIsValid(indexTuple
))
12266 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12267 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12268 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
12271 * Refuse to use a deferrable primary key. This is per SQL spec,
12272 * and there would be a lot of interesting semantic problems if we
12273 * tried to allow it.
12275 if (!indexStruct
->indimmediate
)
12277 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12278 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12279 RelationGetRelationName(pkrel
))));
12281 *indexOid
= indexoid
;
12284 ReleaseSysCache(indexTuple
);
12287 list_free(indexoidlist
);
12290 * Check that we found it
12292 if (!OidIsValid(*indexOid
))
12294 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12295 errmsg("there is no primary key for referenced table \"%s\"",
12296 RelationGetRelationName(pkrel
))));
12298 /* Must get indclass the hard way */
12299 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12300 Anum_pg_index_indclass
);
12301 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12304 * Now build the list of PK attributes from the indkey definition (we
12305 * assume a primary key cannot have expressional elements)
12307 *attnamelist
= NIL
;
12308 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
12310 int pkattno
= indexStruct
->indkey
.values
[i
];
12312 attnums
[i
] = pkattno
;
12313 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
12314 attcollids
[i
] = attnumCollationId(pkrel
, pkattno
);
12315 opclasses
[i
] = indclass
->values
[i
];
12316 *attnamelist
= lappend(*attnamelist
,
12317 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
12320 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12322 ReleaseSysCache(indexTuple
);
12328 * transformFkeyCheckAttrs -
12330 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12331 * reference as part of a foreign key constraint.
12333 * Returns the OID of the unique index supporting the constraint and
12334 * populates the caller-provided 'opclasses' array with the opclasses
12335 * associated with the index columns. Also sets whether the index
12336 * uses WITHOUT OVERLAPS.
12338 * Raises an ERROR on validation failure.
12341 transformFkeyCheckAttrs(Relation pkrel
,
12342 int numattrs
, int16
*attnums
,
12343 bool with_period
, Oid
*opclasses
,
12344 bool *pk_has_without_overlaps
)
12346 Oid indexoid
= InvalidOid
;
12347 bool found
= false;
12348 bool found_deferrable
= false;
12349 List
*indexoidlist
;
12350 ListCell
*indexoidscan
;
12355 * Reject duplicate appearances of columns in the referenced-columns list.
12356 * Such a case is forbidden by the SQL standard, and even if we thought it
12357 * useful to allow it, there would be ambiguity about how to match the
12358 * list to unique indexes (in particular, it'd be unclear which index
12359 * opclass goes with which FK column).
12361 for (i
= 0; i
< numattrs
; i
++)
12363 for (j
= i
+ 1; j
< numattrs
; j
++)
12365 if (attnums
[i
] == attnums
[j
])
12367 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12368 errmsg("foreign key referenced-columns list must not contain duplicates")));
12373 * Get the list of index OIDs for the table from the relcache, and look up
12374 * each one in the pg_index syscache, and match unique indexes to the list
12375 * of attnums we are given.
12377 indexoidlist
= RelationGetIndexList(pkrel
);
12379 foreach(indexoidscan
, indexoidlist
)
12381 HeapTuple indexTuple
;
12382 Form_pg_index indexStruct
;
12384 indexoid
= lfirst_oid(indexoidscan
);
12385 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12386 if (!HeapTupleIsValid(indexTuple
))
12387 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12388 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12391 * Must have the right number of columns; must be unique (or if
12392 * temporal then exclusion instead) and not a partial index; forget it
12393 * if there are any expressions, too. Invalid indexes are out as well.
12395 if (indexStruct
->indnkeyatts
== numattrs
&&
12396 (with_period
? indexStruct
->indisexclusion
: indexStruct
->indisunique
) &&
12397 indexStruct
->indisvalid
&&
12398 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
12399 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
12401 Datum indclassDatum
;
12402 oidvector
*indclass
;
12404 /* Must get indclass the hard way */
12405 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12406 Anum_pg_index_indclass
);
12407 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12410 * The given attnum list may match the index columns in any order.
12411 * Check for a match, and extract the appropriate opclasses while
12414 * We know that attnums[] is duplicate-free per the test at the
12415 * start of this function, and we checked above that the number of
12416 * index columns agrees, so if we find a match for each attnums[]
12417 * entry then we must have a one-to-one match in some order.
12419 for (i
= 0; i
< numattrs
; i
++)
12422 for (j
= 0; j
< numattrs
; j
++)
12424 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
12426 opclasses
[i
] = indclass
->values
[j
];
12434 /* The last attribute in the index must be the PERIOD FK part */
12435 if (found
&& with_period
)
12437 int16 periodattnum
= attnums
[numattrs
- 1];
12439 found
= (periodattnum
== indexStruct
->indkey
.values
[numattrs
- 1]);
12443 * Refuse to use a deferrable unique/primary key. This is per SQL
12444 * spec, and there would be a lot of interesting semantic problems
12445 * if we tried to allow it.
12447 if (found
&& !indexStruct
->indimmediate
)
12450 * Remember that we found an otherwise matching index, so that
12451 * we can generate a more appropriate error message.
12453 found_deferrable
= true;
12457 /* We need to know whether the index has WITHOUT OVERLAPS */
12459 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12461 ReleaseSysCache(indexTuple
);
12468 if (found_deferrable
)
12470 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12471 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12472 RelationGetRelationName(pkrel
))));
12475 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12476 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12477 RelationGetRelationName(pkrel
))));
12480 list_free(indexoidlist
);
12488 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12489 * Caller has equal regard for binary coercibility and for an exact match.
12491 static CoercionPathType
12492 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
12494 CoercionPathType ret
;
12496 if (targetTypeId
== sourceTypeId
)
12498 ret
= COERCION_PATH_RELABELTYPE
;
12499 *funcid
= InvalidOid
;
12503 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
12504 COERCION_IMPLICIT
, funcid
);
12505 if (ret
== COERCION_PATH_NONE
)
12506 /* A previously-relied-upon cast is now gone. */
12507 elog(ERROR
, "could not find cast from %u to %u",
12508 sourceTypeId
, targetTypeId
);
12515 * Permissions checks on the referenced table for ADD FOREIGN KEY
12517 * Note: we have already checked that the user owns the referencing table,
12518 * else we'd have failed much earlier; no additional checks are needed for it.
12521 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
12523 Oid roleid
= GetUserId();
12524 AclResult aclresult
;
12527 /* Okay if we have relation-level REFERENCES permission */
12528 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
12530 if (aclresult
== ACLCHECK_OK
)
12532 /* Else we must have REFERENCES on each column */
12533 for (i
= 0; i
< natts
; i
++)
12535 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
12536 roleid
, ACL_REFERENCES
);
12537 if (aclresult
!= ACLCHECK_OK
)
12538 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
12539 RelationGetRelationName(rel
));
12544 * Scan the existing rows in a table to verify they meet a proposed FK
12547 * Caller must have opened and locked both relations appropriately.
12550 validateForeignKeyConstraint(char *conname
,
12557 TupleTableSlot
*slot
;
12558 TableScanDesc scan
;
12559 Trigger trig
= {0};
12561 MemoryContext oldcxt
;
12562 MemoryContext perTupCxt
;
12565 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
12568 * Build a trigger call structure; we'll need it either way.
12570 trig
.tgoid
= InvalidOid
;
12571 trig
.tgname
= conname
;
12572 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
12573 trig
.tgisinternal
= true;
12574 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
12575 trig
.tgconstrindid
= pkindOid
;
12576 trig
.tgconstraint
= constraintOid
;
12577 trig
.tgdeferrable
= false;
12578 trig
.tginitdeferred
= false;
12579 /* we needn't fill in remaining fields */
12582 * See if we can do it with a single LEFT JOIN query. A false result
12583 * indicates we must proceed with the fire-the-trigger method. We can't do
12584 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12587 if (!hasperiod
&& RI_Initial_Check(&trig
, rel
, pkrel
))
12591 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12592 * if that tuple had just been inserted. If any of those fail, it should
12593 * ereport(ERROR) and that's that.
12595 snapshot
= RegisterSnapshot(GetLatestSnapshot());
12596 slot
= table_slot_create(rel
, NULL
);
12597 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
12599 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
12600 "validateForeignKeyConstraint",
12601 ALLOCSET_SMALL_SIZES
);
12602 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
12604 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
12606 LOCAL_FCINFO(fcinfo
, 0);
12607 TriggerData trigdata
= {0};
12609 CHECK_FOR_INTERRUPTS();
12612 * Make a call to the trigger function
12614 * No parameters are passed, but we do set a context
12616 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
12619 * We assume RI_FKey_check_ins won't look at flinfo...
12621 trigdata
.type
= T_TriggerData
;
12622 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
12623 trigdata
.tg_relation
= rel
;
12624 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
12625 trigdata
.tg_trigslot
= slot
;
12626 trigdata
.tg_trigger
= &trig
;
12628 fcinfo
->context
= (Node
*) &trigdata
;
12630 RI_FKey_check_ins(fcinfo
);
12632 MemoryContextReset(perTupCxt
);
12635 MemoryContextSwitchTo(oldcxt
);
12636 MemoryContextDelete(perTupCxt
);
12637 table_endscan(scan
);
12638 UnregisterSnapshot(snapshot
);
12639 ExecDropSingleTupleTableSlot(slot
);
12643 * CreateFKCheckTrigger
12644 * Creates the insert (on_insert=true) or update "check" trigger that
12645 * implements a given foreign key
12647 * Returns the OID of the so created trigger.
12650 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
12651 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
12654 ObjectAddress trigAddress
;
12655 CreateTrigStmt
*fk_trigger
;
12658 * Note: for a self-referential FK (referencing and referenced tables are
12659 * the same), it is important that the ON UPDATE action fires before the
12660 * CHECK action, since both triggers will fire on the same row during an
12661 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12662 * state of the row. Triggers fire in name order, so we ensure this by
12663 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12664 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12666 fk_trigger
= makeNode(CreateTrigStmt
);
12667 fk_trigger
->replace
= false;
12668 fk_trigger
->isconstraint
= true;
12669 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
12670 fk_trigger
->relation
= NULL
;
12672 /* Either ON INSERT or ON UPDATE */
12675 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
12676 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
12680 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
12681 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12684 fk_trigger
->args
= NIL
;
12685 fk_trigger
->row
= true;
12686 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12687 fk_trigger
->columns
= NIL
;
12688 fk_trigger
->whenClause
= NULL
;
12689 fk_trigger
->transitionRels
= NIL
;
12690 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12691 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12692 fk_trigger
->constrrel
= NULL
;
12694 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
12695 constraintOid
, indexOid
, InvalidOid
,
12696 parentTrigOid
, NULL
, true, false);
12698 /* Make changes-so-far visible */
12699 CommandCounterIncrement();
12701 return trigAddress
.objectId
;
12705 * createForeignKeyActionTriggers
12706 * Create the referenced-side "action" triggers that implement a foreign
12709 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12713 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
12714 Oid constraintOid
, Oid indexOid
,
12715 Oid parentDelTrigger
, Oid parentUpdTrigger
,
12716 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
12718 CreateTrigStmt
*fk_trigger
;
12719 ObjectAddress trigAddress
;
12722 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12723 * DELETE action on the referenced table.
12725 fk_trigger
= makeNode(CreateTrigStmt
);
12726 fk_trigger
->replace
= false;
12727 fk_trigger
->isconstraint
= true;
12728 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12729 fk_trigger
->relation
= NULL
;
12730 fk_trigger
->args
= NIL
;
12731 fk_trigger
->row
= true;
12732 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12733 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
12734 fk_trigger
->columns
= NIL
;
12735 fk_trigger
->whenClause
= NULL
;
12736 fk_trigger
->transitionRels
= NIL
;
12737 fk_trigger
->constrrel
= NULL
;
12739 switch (fkconstraint
->fk_del_action
)
12741 case FKCONSTR_ACTION_NOACTION
:
12742 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12743 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12744 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
12746 case FKCONSTR_ACTION_RESTRICT
:
12747 fk_trigger
->deferrable
= false;
12748 fk_trigger
->initdeferred
= false;
12749 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
12751 case FKCONSTR_ACTION_CASCADE
:
12752 fk_trigger
->deferrable
= false;
12753 fk_trigger
->initdeferred
= false;
12754 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
12756 case FKCONSTR_ACTION_SETNULL
:
12757 fk_trigger
->deferrable
= false;
12758 fk_trigger
->initdeferred
= false;
12759 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
12761 case FKCONSTR_ACTION_SETDEFAULT
:
12762 fk_trigger
->deferrable
= false;
12763 fk_trigger
->initdeferred
= false;
12764 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
12767 elog(ERROR
, "unrecognized FK action type: %d",
12768 (int) fkconstraint
->fk_del_action
);
12772 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12773 RelationGetRelid(rel
),
12774 constraintOid
, indexOid
, InvalidOid
,
12775 parentDelTrigger
, NULL
, true, false);
12777 *deleteTrigOid
= trigAddress
.objectId
;
12779 /* Make changes-so-far visible */
12780 CommandCounterIncrement();
12783 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12784 * UPDATE action on the referenced table.
12786 fk_trigger
= makeNode(CreateTrigStmt
);
12787 fk_trigger
->replace
= false;
12788 fk_trigger
->isconstraint
= true;
12789 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12790 fk_trigger
->relation
= NULL
;
12791 fk_trigger
->args
= NIL
;
12792 fk_trigger
->row
= true;
12793 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12794 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12795 fk_trigger
->columns
= NIL
;
12796 fk_trigger
->whenClause
= NULL
;
12797 fk_trigger
->transitionRels
= NIL
;
12798 fk_trigger
->constrrel
= NULL
;
12800 switch (fkconstraint
->fk_upd_action
)
12802 case FKCONSTR_ACTION_NOACTION
:
12803 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12804 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12805 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
12807 case FKCONSTR_ACTION_RESTRICT
:
12808 fk_trigger
->deferrable
= false;
12809 fk_trigger
->initdeferred
= false;
12810 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
12812 case FKCONSTR_ACTION_CASCADE
:
12813 fk_trigger
->deferrable
= false;
12814 fk_trigger
->initdeferred
= false;
12815 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
12817 case FKCONSTR_ACTION_SETNULL
:
12818 fk_trigger
->deferrable
= false;
12819 fk_trigger
->initdeferred
= false;
12820 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
12822 case FKCONSTR_ACTION_SETDEFAULT
:
12823 fk_trigger
->deferrable
= false;
12824 fk_trigger
->initdeferred
= false;
12825 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
12828 elog(ERROR
, "unrecognized FK action type: %d",
12829 (int) fkconstraint
->fk_upd_action
);
12833 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12834 RelationGetRelid(rel
),
12835 constraintOid
, indexOid
, InvalidOid
,
12836 parentUpdTrigger
, NULL
, true, false);
12838 *updateTrigOid
= trigAddress
.objectId
;
12842 * createForeignKeyCheckTriggers
12843 * Create the referencing-side "check" triggers that implement a foreign
12846 * Returns the OIDs of the so created triggers in *insertTrigOid and
12850 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
12851 Constraint
*fkconstraint
, Oid constraintOid
,
12853 Oid parentInsTrigger
, Oid parentUpdTrigger
,
12854 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
12856 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12857 constraintOid
, indexOid
,
12858 parentInsTrigger
, true);
12859 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12860 constraintOid
, indexOid
,
12861 parentUpdTrigger
, false);
12865 * ALTER TABLE DROP CONSTRAINT
12867 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12870 ATExecDropConstraint(Relation rel
, const char *constrName
,
12871 DropBehavior behavior
, bool recurse
,
12872 bool missing_ok
, LOCKMODE lockmode
)
12876 ScanKeyData skey
[3];
12878 bool found
= false;
12880 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12883 * Find and drop the target constraint
12885 ScanKeyInit(&skey
[0],
12886 Anum_pg_constraint_conrelid
,
12887 BTEqualStrategyNumber
, F_OIDEQ
,
12888 ObjectIdGetDatum(RelationGetRelid(rel
)));
12889 ScanKeyInit(&skey
[1],
12890 Anum_pg_constraint_contypid
,
12891 BTEqualStrategyNumber
, F_OIDEQ
,
12892 ObjectIdGetDatum(InvalidOid
));
12893 ScanKeyInit(&skey
[2],
12894 Anum_pg_constraint_conname
,
12895 BTEqualStrategyNumber
, F_NAMEEQ
,
12896 CStringGetDatum(constrName
));
12897 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12898 true, NULL
, 3, skey
);
12900 /* There can be at most one matching row */
12901 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12903 dropconstraint_internal(rel
, tuple
, behavior
, recurse
, false,
12904 missing_ok
, lockmode
);
12908 systable_endscan(scan
);
12914 errcode(ERRCODE_UNDEFINED_OBJECT
),
12915 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12916 constrName
, RelationGetRelationName(rel
)));
12919 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12920 constrName
, RelationGetRelationName(rel
)));
12923 table_close(conrel
, RowExclusiveLock
);
12927 * Remove a constraint, using its pg_constraint tuple
12929 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12932 * Returns the address of the constraint being removed.
12934 static ObjectAddress
12935 dropconstraint_internal(Relation rel
, HeapTuple constraintTup
, DropBehavior behavior
,
12936 bool recurse
, bool recursing
, bool missing_ok
,
12940 Form_pg_constraint con
;
12941 ObjectAddress conobj
;
12943 bool is_no_inherit_constraint
= false;
12945 char *colname
= NULL
;
12947 /* Guard against stack overflow due to overly deep inheritance tree. */
12948 check_stack_depth();
12950 /* At top level, permission check was done in ATPrepCmd, else do it */
12952 ATSimplePermissions(AT_DropConstraint
, rel
,
12953 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
12955 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12957 con
= (Form_pg_constraint
) GETSTRUCT(constraintTup
);
12958 constrName
= NameStr(con
->conname
);
12960 /* Don't allow drop of inherited constraints */
12961 if (con
->coninhcount
> 0 && !recursing
)
12963 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12964 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12965 constrName
, RelationGetRelationName(rel
))));
12968 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
12970 * While doing that, we're in a good position to disallow dropping a not-
12971 * null constraint underneath a primary key, a replica identity index, or
12972 * a generated identity column.
12974 if (con
->contype
== CONSTRAINT_NOTNULL
)
12976 Relation attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
12977 AttrNumber attnum
= extractNotNullColumn(constraintTup
);
12978 Bitmapset
*pkattrs
;
12979 Bitmapset
*irattrs
;
12981 Form_pg_attribute attForm
;
12983 /* save column name for recursion step */
12984 colname
= get_attname(RelationGetRelid(rel
), attnum
, false);
12987 * Disallow if it's in the primary key. For partitioned tables we
12988 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
12989 * return NULL if the primary key is invalid; but we still need to
12990 * protect not-null constraints under such a constraint, so check the
12993 pkattrs
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_PRIMARY_KEY
);
12995 if (pkattrs
== NULL
&&
12996 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
12998 Oid pkindex
= RelationGetPrimaryKeyIndex(rel
, true);
13000 if (OidIsValid(pkindex
))
13002 Relation pk
= relation_open(pkindex
, AccessShareLock
);
13005 for (int i
= 0; i
< pk
->rd_index
->indnkeyatts
; i
++)
13006 pkattrs
= bms_add_member(pkattrs
, pk
->rd_index
->indkey
.values
[i
]);
13008 relation_close(pk
, AccessShareLock
);
13013 bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
, pkattrs
))
13015 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13016 errmsg("column \"%s\" is in a primary key",
13017 get_attname(RelationGetRelid(rel
), attnum
, false)));
13019 /* Disallow if it's in the replica identity */
13020 irattrs
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_IDENTITY_KEY
);
13021 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
, irattrs
))
13023 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13024 errmsg("column \"%s\" is in index used as replica identity",
13025 get_attname(RelationGetRelid(rel
), attnum
, false)));
13027 /* Disallow if it's a GENERATED AS IDENTITY column */
13028 atttup
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
13029 if (!HeapTupleIsValid(atttup
))
13030 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
13031 attnum
, RelationGetRelid(rel
));
13032 attForm
= (Form_pg_attribute
) GETSTRUCT(atttup
);
13033 if (attForm
->attidentity
!= '\0')
13035 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
13036 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13037 get_attname(RelationGetRelid(rel
), attnum
,
13039 RelationGetRelationName(rel
)));
13041 /* All good -- reset attnotnull if needed */
13042 if (attForm
->attnotnull
)
13044 attForm
->attnotnull
= false;
13045 CatalogTupleUpdate(attrel
, &atttup
->t_self
, atttup
);
13048 table_close(attrel
, RowExclusiveLock
);
13051 is_no_inherit_constraint
= con
->connoinherit
;
13054 * If it's a foreign-key constraint, we'd better lock the referenced table
13055 * and check that that's not in use, just as we've already done for the
13056 * constrained table (else we might, eg, be dropping a trigger that has
13057 * unfired events). But we can/must skip that in the self-referential
13060 if (con
->contype
== CONSTRAINT_FOREIGN
&&
13061 con
->confrelid
!= RelationGetRelid(rel
))
13065 /* Must match lock taken by RemoveTriggerById: */
13066 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
13067 CheckAlterTableIsSafe(frel
);
13068 table_close(frel
, NoLock
);
13072 * Perform the actual constraint deletion
13074 ObjectAddressSet(conobj
, ConstraintRelationId
, con
->oid
);
13075 performDeletion(&conobj
, behavior
, 0);
13078 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13079 * are dropped via the dependency mechanism, so we're done here.
13081 if (con
->contype
!= CONSTRAINT_CHECK
&&
13082 con
->contype
!= CONSTRAINT_NOTNULL
&&
13083 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
13085 table_close(conrel
, RowExclusiveLock
);
13090 * Propagate to children as appropriate. Unlike most other ALTER
13091 * routines, we have to do this one level of recursion at a time; we can't
13092 * use find_all_inheritors to do it in one pass.
13094 if (!is_no_inherit_constraint
)
13095 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
13099 foreach_oid(childrelid
, children
)
13103 Form_pg_constraint childcon
;
13105 /* find_inheritance_children already got lock */
13106 childrel
= table_open(childrelid
, NoLock
);
13107 CheckAlterTableIsSafe(childrel
);
13110 * We search for not-null constraints by column name, and others by
13113 if (con
->contype
== CONSTRAINT_NOTNULL
)
13115 tuple
= findNotNullConstraint(childrelid
, colname
);
13116 if (!HeapTupleIsValid(tuple
))
13117 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13118 colname
, RelationGetRelid(childrel
));
13123 ScanKeyData skey
[3];
13125 ScanKeyInit(&skey
[0],
13126 Anum_pg_constraint_conrelid
,
13127 BTEqualStrategyNumber
, F_OIDEQ
,
13128 ObjectIdGetDatum(childrelid
));
13129 ScanKeyInit(&skey
[1],
13130 Anum_pg_constraint_contypid
,
13131 BTEqualStrategyNumber
, F_OIDEQ
,
13132 ObjectIdGetDatum(InvalidOid
));
13133 ScanKeyInit(&skey
[2],
13134 Anum_pg_constraint_conname
,
13135 BTEqualStrategyNumber
, F_NAMEEQ
,
13136 CStringGetDatum(constrName
));
13137 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
13138 true, NULL
, 3, skey
);
13139 /* There can only be one, so no need to loop */
13140 tuple
= systable_getnext(scan
);
13141 if (!HeapTupleIsValid(tuple
))
13143 (errcode(ERRCODE_UNDEFINED_OBJECT
),
13144 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13146 RelationGetRelationName(childrel
))));
13147 tuple
= heap_copytuple(tuple
);
13148 systable_endscan(scan
);
13151 childcon
= (Form_pg_constraint
) GETSTRUCT(tuple
);
13153 /* Right now only CHECK and not-null constraints can be inherited */
13154 if (childcon
->contype
!= CONSTRAINT_CHECK
&&
13155 childcon
->contype
!= CONSTRAINT_NOTNULL
)
13156 elog(ERROR
, "inherited constraint is not a CHECK or not-null constraint");
13158 if (childcon
->coninhcount
<= 0) /* shouldn't happen */
13159 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
13160 childrelid
, NameStr(childcon
->conname
));
13165 * If the child constraint has other definition sources, just
13166 * decrement its inheritance count; if not, recurse to delete it.
13168 if (childcon
->coninhcount
== 1 && !childcon
->conislocal
)
13170 /* Time to delete this child constraint, too */
13171 dropconstraint_internal(childrel
, tuple
, behavior
,
13172 recurse
, true, missing_ok
,
13177 /* Child constraint must survive my deletion */
13178 childcon
->coninhcount
--;
13179 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13181 /* Make update visible */
13182 CommandCounterIncrement();
13188 * If we were told to drop ONLY in this table (no recursion) and
13189 * there are no further parents for this constraint, we need to
13190 * mark the inheritors' constraints as locally defined rather than
13193 childcon
->coninhcount
--;
13194 if (childcon
->coninhcount
== 0)
13195 childcon
->conislocal
= true;
13197 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13199 /* Make update visible */
13200 CommandCounterIncrement();
13203 heap_freetuple(tuple
);
13205 table_close(childrel
, NoLock
);
13208 table_close(conrel
, RowExclusiveLock
);
13214 * ALTER COLUMN TYPE
13216 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13217 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13218 * transformed (and must be, because we rely on some transformed fields).
13220 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13221 * table will be done "in parallel" during phase 3, so all the USING
13222 * expressions should be parsed assuming the original column types. Also,
13223 * this allows a USING expression to refer to a field that will be dropped.
13225 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13226 * the first two execution steps in phase 2; they must not see the effects
13227 * of any other subcommand types, since the USING expressions are parsed
13228 * against the unmodified table's state.
13231 ATPrepAlterColumnType(List
**wqueue
,
13232 AlteredTableInfo
*tab
, Relation rel
,
13233 bool recurse
, bool recursing
,
13234 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
13235 AlterTableUtilityContext
*context
)
13237 char *colName
= cmd
->name
;
13238 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13239 TypeName
*typeName
= def
->typeName
;
13240 Node
*transform
= def
->cooked_default
;
13242 Form_pg_attribute attTup
;
13245 int32 targettypmod
;
13247 NewColumnValue
*newval
;
13248 ParseState
*pstate
= make_parsestate(NULL
);
13249 AclResult aclresult
;
13252 if (rel
->rd_rel
->reloftype
&& !recursing
)
13254 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13255 errmsg("cannot alter column type of typed table")));
13257 /* lookup the attribute so we can check inheritance status */
13258 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
13259 if (!HeapTupleIsValid(tuple
))
13261 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13262 errmsg("column \"%s\" of relation \"%s\" does not exist",
13263 colName
, RelationGetRelationName(rel
))));
13264 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
13265 attnum
= attTup
->attnum
;
13267 /* Can't alter a system attribute */
13270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13271 errmsg("cannot alter system column \"%s\"",
13275 * Cannot specify USING when altering type of a generated column, because
13276 * that would violate the generation expression.
13278 if (attTup
->attgenerated
&& def
->cooked_default
)
13280 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
13281 errmsg("cannot specify USING when altering type of generated column"),
13282 errdetail("Column \"%s\" is a generated column.", colName
)));
13285 * Don't alter inherited columns. At outer level, there had better not be
13286 * any inherited definition; when recursing, we assume this was checked at
13287 * the parent level (see below).
13289 if (attTup
->attinhcount
> 0 && !recursing
)
13291 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13292 errmsg("cannot alter inherited column \"%s\"",
13295 /* Don't alter columns used in the partition key */
13296 if (has_partition_attrs(rel
,
13297 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
13300 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13301 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13302 colName
, RelationGetRelationName(rel
))));
13304 /* Look up the target type */
13305 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
13307 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
13308 if (aclresult
!= ACLCHECK_OK
)
13309 aclcheck_error_type(aclresult
, targettype
);
13311 /* And the collation */
13312 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13314 /* make sure datatype is legal for a column */
13315 CheckAttributeType(colName
, targettype
, targetcollid
,
13316 list_make1_oid(rel
->rd_rel
->reltype
),
13319 if (tab
->relkind
== RELKIND_RELATION
||
13320 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
13323 * Set up an expression to transform the old data value to the new
13324 * type. If a USING option was given, use the expression as
13325 * transformed by transformAlterTableStmt, else just take the old
13326 * value and try to coerce it. We do this first so that type
13327 * incompatibility can be detected before we waste effort, and because
13328 * we need the expression to be parsed against the original table row
13333 transform
= (Node
*) makeVar(1, attnum
,
13334 attTup
->atttypid
, attTup
->atttypmod
,
13335 attTup
->attcollation
,
13339 transform
= coerce_to_target_type(pstate
,
13340 transform
, exprType(transform
),
13341 targettype
, targettypmod
,
13342 COERCION_ASSIGNMENT
,
13343 COERCE_IMPLICIT_CAST
,
13345 if (transform
== NULL
)
13347 /* error text depends on whether USING was specified or not */
13348 if (def
->cooked_default
!= NULL
)
13350 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13351 errmsg("result of USING clause for column \"%s\""
13352 " cannot be cast automatically to type %s",
13353 colName
, format_type_be(targettype
)),
13354 errhint("You might need to add an explicit cast.")));
13357 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13358 errmsg("column \"%s\" cannot be cast automatically to type %s",
13359 colName
, format_type_be(targettype
)),
13360 !attTup
->attgenerated
?
13361 /* translator: USING is SQL, don't translate it */
13362 errhint("You might need to specify \"USING %s::%s\".",
13363 quote_identifier(colName
),
13364 format_type_with_typemod(targettype
,
13365 targettypmod
)) : 0));
13368 /* Fix collations after all else */
13369 assign_expr_collations(pstate
, transform
);
13371 /* Plan the expr now so we can accurately assess the need to rewrite. */
13372 transform
= (Node
*) expression_planner((Expr
*) transform
);
13375 * Add a work queue item to make ATRewriteTable update the column
13378 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
13379 newval
->attnum
= attnum
;
13380 newval
->expr
= (Expr
*) transform
;
13381 newval
->is_generated
= false;
13383 tab
->newvals
= lappend(tab
->newvals
, newval
);
13384 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
13385 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
13387 else if (transform
)
13389 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13390 errmsg("\"%s\" is not a table",
13391 RelationGetRelationName(rel
))));
13393 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
13396 * For relations without storage, do this check now. Regular tables
13397 * will check it later when the table is being rewritten.
13399 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
13402 ReleaseSysCache(tuple
);
13405 * Recurse manually by queueing a new command for each child, if
13406 * necessary. We cannot apply ATSimpleRecursion here because we need to
13407 * remap attribute numbers in the USING expression, if any.
13409 * If we are told not to recurse, there had better not be any child
13410 * tables; else the alter would put them out of step.
13414 Oid relid
= RelationGetRelid(rel
);
13420 child_oids
= find_all_inheritors(relid
, lockmode
,
13421 &child_numparents
);
13424 * find_all_inheritors does the recursive search of the inheritance
13425 * hierarchy, so all we have to do is process all of the relids in the
13426 * list that it returns.
13428 forboth(lo
, child_oids
, li
, child_numparents
)
13430 Oid childrelid
= lfirst_oid(lo
);
13431 int numparents
= lfirst_int(li
);
13433 HeapTuple childtuple
;
13434 Form_pg_attribute childattTup
;
13436 if (childrelid
== relid
)
13439 /* find_all_inheritors already got lock */
13440 childrel
= relation_open(childrelid
, NoLock
);
13441 CheckAlterTableIsSafe(childrel
);
13444 * Verify that the child doesn't have any inherited definitions of
13445 * this column that came from outside this inheritance hierarchy.
13446 * (renameatt makes a similar test, though in a different way
13447 * because of its different recursion mechanism.)
13449 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
13451 if (!HeapTupleIsValid(childtuple
))
13453 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13454 errmsg("column \"%s\" of relation \"%s\" does not exist",
13455 colName
, RelationGetRelationName(childrel
))));
13456 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
13458 if (childattTup
->attinhcount
> numparents
)
13460 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13461 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13462 colName
, RelationGetRelationName(childrel
))));
13464 ReleaseSysCache(childtuple
);
13467 * Remap the attribute numbers. If no USING expression was
13468 * specified, there is no need for this step.
13470 if (def
->cooked_default
)
13473 bool found_whole_row
;
13475 /* create a copy to scribble on */
13476 cmd
= copyObject(cmd
);
13478 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
13479 RelationGetDescr(rel
),
13481 ((ColumnDef
*) cmd
->def
)->cooked_default
=
13482 map_variable_attnos(def
->cooked_default
,
13485 InvalidOid
, &found_whole_row
);
13486 if (found_whole_row
)
13488 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13489 errmsg("cannot convert whole-row table reference"),
13490 errdetail("USING expression contains a whole-row table reference.")));
13493 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
13494 relation_close(childrel
, NoLock
);
13497 else if (!recursing
&&
13498 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
13500 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13501 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13504 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
13505 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
13509 * When the data type of a column is changed, a rewrite might not be required
13510 * if the new type is sufficiently identical to the old one, and the USING
13511 * clause isn't trying to insert some other value. It's safe to skip the
13512 * rewrite in these cases:
13514 * - the old type is binary coercible to the new type
13515 * - the new type is an unconstrained domain over the old type
13516 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13518 * In the case of a constrained domain, we could get by with scanning the
13519 * table and checking the constraint rather than actually rewriting it, but we
13520 * don't currently try to do that.
13523 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
13525 Assert(expr
!= NULL
);
13529 /* only one varno, so no need to check that */
13530 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
13532 else if (IsA(expr
, RelabelType
))
13533 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
13534 else if (IsA(expr
, CoerceToDomain
))
13536 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
13538 if (DomainHasConstraints(d
->resulttype
))
13540 expr
= (Node
*) d
->arg
;
13542 else if (IsA(expr
, FuncExpr
))
13544 FuncExpr
*f
= (FuncExpr
*) expr
;
13548 case F_TIMESTAMPTZ_TIMESTAMP
:
13549 case F_TIMESTAMP_TIMESTAMPTZ
:
13550 if (TimestampTimestampTzRequiresRewrite())
13553 expr
= linitial(f
->args
);
13565 * ALTER COLUMN .. SET DATA TYPE
13567 * Return the address of the modified column.
13569 static ObjectAddress
13570 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
13571 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
13573 char *colName
= cmd
->name
;
13574 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13575 TypeName
*typeName
= def
->typeName
;
13577 Form_pg_attribute attTup
,
13580 HeapTuple typeTuple
;
13581 Form_pg_type tform
;
13583 int32 targettypmod
;
13586 Relation attrelation
;
13588 ScanKeyData key
[3];
13591 ObjectAddress address
;
13594 * Clear all the missing values if we're rewriting the table, since this
13595 * renders them pointless.
13601 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
13602 RelationClearMissing(newrel
);
13603 relation_close(newrel
, NoLock
);
13604 /* make sure we don't conflict with later attribute modifications */
13605 CommandCounterIncrement();
13608 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
13610 /* Look up the target column */
13611 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
13612 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
13614 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13615 errmsg("column \"%s\" of relation \"%s\" does not exist",
13616 colName
, RelationGetRelationName(rel
))));
13617 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13618 attnum
= attTup
->attnum
;
13619 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
13621 /* Check for multiple ALTER TYPE on same column --- can't cope */
13622 if (attTup
->atttypid
!= attOldTup
->atttypid
||
13623 attTup
->atttypmod
!= attOldTup
->atttypmod
)
13625 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13626 errmsg("cannot alter type of column \"%s\" twice",
13629 /* Look up the target type (should not fail, since prep found it) */
13630 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
13631 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
13632 targettype
= tform
->oid
;
13633 /* And the collation */
13634 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13637 * If there is a default expression for the column, get it and ensure we
13638 * can coerce it to the new datatype. (We must do this before changing
13639 * the column type, because build_column_default itself will try to
13640 * coerce, and will not issue the error message we want if it fails.)
13642 * We remove any implicit coercion steps at the top level of the old
13643 * default expression; this has been agreed to satisfy the principle of
13644 * least surprise. (The conversion to the new column type should act like
13645 * it started from what the user sees as the stored expression, and the
13646 * implicit coercions aren't going to be shown.)
13648 if (attTup
->atthasdef
)
13650 defaultexpr
= build_column_default(rel
, attnum
);
13651 Assert(defaultexpr
);
13652 defaultexpr
= strip_implicit_coercions(defaultexpr
);
13653 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
13654 defaultexpr
, exprType(defaultexpr
),
13655 targettype
, targettypmod
,
13656 COERCION_ASSIGNMENT
,
13657 COERCE_IMPLICIT_CAST
,
13659 if (defaultexpr
== NULL
)
13661 if (attTup
->attgenerated
)
13663 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13664 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13665 colName
, format_type_be(targettype
))));
13668 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13669 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13670 colName
, format_type_be(targettype
))));
13674 defaultexpr
= NULL
;
13677 * Find everything that depends on the column (constraints, indexes, etc),
13678 * and record enough information to let us recreate the objects.
13680 * The actual recreation does not happen here, but only after we have
13681 * performed all the individual ALTER TYPE operations. We have to save
13682 * the info before executing ALTER TYPE, though, else the deparser will
13685 RememberAllDependentForRebuilding(tab
, AT_AlterColumnType
, rel
, attnum
, colName
);
13688 * Now scan for dependencies of this column on other things. The only
13689 * things we should find are the dependency on the column datatype and
13690 * possibly a collation dependency. Those can be removed.
13692 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13694 ScanKeyInit(&key
[0],
13695 Anum_pg_depend_classid
,
13696 BTEqualStrategyNumber
, F_OIDEQ
,
13697 ObjectIdGetDatum(RelationRelationId
));
13698 ScanKeyInit(&key
[1],
13699 Anum_pg_depend_objid
,
13700 BTEqualStrategyNumber
, F_OIDEQ
,
13701 ObjectIdGetDatum(RelationGetRelid(rel
)));
13702 ScanKeyInit(&key
[2],
13703 Anum_pg_depend_objsubid
,
13704 BTEqualStrategyNumber
, F_INT4EQ
,
13705 Int32GetDatum((int32
) attnum
));
13707 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
13710 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13712 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13713 ObjectAddress foundObject
;
13715 foundObject
.classId
= foundDep
->refclassid
;
13716 foundObject
.objectId
= foundDep
->refobjid
;
13717 foundObject
.objectSubId
= foundDep
->refobjsubid
;
13719 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
13720 elog(ERROR
, "found unexpected dependency type '%c'",
13721 foundDep
->deptype
);
13722 if (!(foundDep
->refclassid
== TypeRelationId
&&
13723 foundDep
->refobjid
== attTup
->atttypid
) &&
13724 !(foundDep
->refclassid
== CollationRelationId
&&
13725 foundDep
->refobjid
== attTup
->attcollation
))
13726 elog(ERROR
, "found unexpected dependency for column: %s",
13727 getObjectDescription(&foundObject
, false));
13729 CatalogTupleDelete(depRel
, &depTup
->t_self
);
13732 systable_endscan(scan
);
13734 table_close(depRel
, RowExclusiveLock
);
13737 * Here we go --- change the recorded column type and collation. (Note
13738 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13739 * fix up the missing value if any.
13741 if (attTup
->atthasmissing
)
13746 /* if rewrite is true the missing value should already be cleared */
13747 Assert(tab
->rewrite
== 0);
13749 /* Get the missing value datum */
13750 missingval
= heap_getattr(heapTup
,
13751 Anum_pg_attribute_attmissingval
,
13752 attrelation
->rd_att
,
13755 /* if it's a null array there is nothing to do */
13760 * Get the datum out of the array and repack it in a new array
13761 * built with the new type data. We assume that since the table
13762 * doesn't need rewriting, the actual Datum doesn't need to be
13763 * changed, only the array metadata.
13768 Datum valuesAtt
[Natts_pg_attribute
] = {0};
13769 bool nullsAtt
[Natts_pg_attribute
] = {0};
13770 bool replacesAtt
[Natts_pg_attribute
] = {0};
13773 missingval
= array_get_element(missingval
,
13781 missingval
= PointerGetDatum(construct_array(&missingval
,
13788 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
13789 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
13790 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
13792 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
13793 valuesAtt
, nullsAtt
, replacesAtt
);
13794 heap_freetuple(heapTup
);
13796 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13800 attTup
->atttypid
= targettype
;
13801 attTup
->atttypmod
= targettypmod
;
13802 attTup
->attcollation
= targetcollid
;
13803 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
13805 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
13806 errmsg("too many array dimensions"));
13807 attTup
->attndims
= list_length(typeName
->arrayBounds
);
13808 attTup
->attlen
= tform
->typlen
;
13809 attTup
->attbyval
= tform
->typbyval
;
13810 attTup
->attalign
= tform
->typalign
;
13811 attTup
->attstorage
= tform
->typstorage
;
13812 attTup
->attcompression
= InvalidCompressionMethod
;
13814 ReleaseSysCache(typeTuple
);
13816 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
13818 table_close(attrelation
, RowExclusiveLock
);
13820 /* Install dependencies on new datatype and collation */
13821 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
13822 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
13825 * Drop any pg_statistic entry for the column, since it's now wrong type
13827 RemoveStatistics(RelationGetRelid(rel
), attnum
);
13829 InvokeObjectPostAlterHook(RelationRelationId
,
13830 RelationGetRelid(rel
), attnum
);
13833 * Update the default, if present, by brute force --- remove and re-add
13834 * the default. Probably unsafe to take shortcuts, since the new version
13835 * may well have additional dependencies. (It's okay to do this now,
13836 * rather than after other ALTER TYPE commands, since the default won't
13837 * depend on other column types.)
13842 * If it's a GENERATED default, drop its dependency records, in
13843 * particular its INTERNAL dependency on the column, which would
13844 * otherwise cause dependency.c to refuse to perform the deletion.
13846 if (attTup
->attgenerated
)
13848 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
13850 if (!OidIsValid(attrdefoid
))
13851 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
13852 RelationGetRelid(rel
), attnum
);
13853 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
13857 * Make updates-so-far visible, particularly the new pg_attribute row
13858 * which will be updated again.
13860 CommandCounterIncrement();
13863 * We use RESTRICT here for safety, but at present we do not expect
13864 * anything to depend on the default.
13866 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
13869 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
13872 ObjectAddressSubSet(address
, RelationRelationId
,
13873 RelationGetRelid(rel
), attnum
);
13876 heap_freetuple(heapTup
);
13882 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13883 * that depends on the column (constraints, indexes, etc), and record enough
13884 * information to let us recreate the objects.
13887 RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
13888 Relation rel
, AttrNumber attnum
, const char *colName
)
13891 ScanKeyData key
[3];
13895 Assert(subtype
== AT_AlterColumnType
|| subtype
== AT_SetExpression
);
13897 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13899 ScanKeyInit(&key
[0],
13900 Anum_pg_depend_refclassid
,
13901 BTEqualStrategyNumber
, F_OIDEQ
,
13902 ObjectIdGetDatum(RelationRelationId
));
13903 ScanKeyInit(&key
[1],
13904 Anum_pg_depend_refobjid
,
13905 BTEqualStrategyNumber
, F_OIDEQ
,
13906 ObjectIdGetDatum(RelationGetRelid(rel
)));
13907 ScanKeyInit(&key
[2],
13908 Anum_pg_depend_refobjsubid
,
13909 BTEqualStrategyNumber
, F_INT4EQ
,
13910 Int32GetDatum((int32
) attnum
));
13912 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
13915 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13917 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13918 ObjectAddress foundObject
;
13920 foundObject
.classId
= foundDep
->classid
;
13921 foundObject
.objectId
= foundDep
->objid
;
13922 foundObject
.objectSubId
= foundDep
->objsubid
;
13924 switch (foundObject
.classId
)
13926 case RelationRelationId
:
13928 char relKind
= get_rel_relkind(foundObject
.objectId
);
13930 if (relKind
== RELKIND_INDEX
||
13931 relKind
== RELKIND_PARTITIONED_INDEX
)
13933 Assert(foundObject
.objectSubId
== 0);
13934 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
13936 else if (relKind
== RELKIND_SEQUENCE
)
13939 * This must be a SERIAL column's sequence. We need
13940 * not do anything to it.
13942 Assert(foundObject
.objectSubId
== 0);
13946 /* Not expecting any other direct dependencies... */
13947 elog(ERROR
, "unexpected object depending on column: %s",
13948 getObjectDescription(&foundObject
, false));
13953 case ConstraintRelationId
:
13954 Assert(foundObject
.objectSubId
== 0);
13955 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
13958 case ProcedureRelationId
:
13961 * A new-style SQL function can depend on a column, if that
13962 * column is referenced in the parsed function body. Ideally
13963 * we'd automatically update the function by deparsing and
13964 * reparsing it, but that's risky and might well fail anyhow.
13967 * This is only a problem for AT_AlterColumnType, not
13968 * AT_SetExpression.
13970 if (subtype
== AT_AlterColumnType
)
13972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13973 errmsg("cannot alter type of a column used by a function or procedure"),
13974 errdetail("%s depends on column \"%s\"",
13975 getObjectDescription(&foundObject
, false),
13979 case RewriteRelationId
:
13982 * View/rule bodies have pretty much the same issues as
13983 * function bodies. FIXME someday.
13985 if (subtype
== AT_AlterColumnType
)
13987 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13988 errmsg("cannot alter type of a column used by a view or rule"),
13989 errdetail("%s depends on column \"%s\"",
13990 getObjectDescription(&foundObject
, false),
13994 case TriggerRelationId
:
13997 * A trigger can depend on a column because the column is
13998 * specified as an update target, or because the column is
13999 * used in the trigger's WHEN condition. The first case would
14000 * not require any extra work, but the second case would
14001 * require updating the WHEN expression, which has the same
14002 * issues as above. Since we can't easily tell which case
14003 * applies, we punt for both. FIXME someday.
14005 if (subtype
== AT_AlterColumnType
)
14007 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14008 errmsg("cannot alter type of a column used in a trigger definition"),
14009 errdetail("%s depends on column \"%s\"",
14010 getObjectDescription(&foundObject
, false),
14014 case PolicyRelationId
:
14017 * A policy can depend on a column because the column is
14018 * specified in the policy's USING or WITH CHECK qual
14019 * expressions. It might be possible to rewrite and recheck
14020 * the policy expression, but punt for now. It's certainly
14021 * easy enough to remove and recreate the policy; still, FIXME
14024 if (subtype
== AT_AlterColumnType
)
14026 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14027 errmsg("cannot alter type of a column used in a policy definition"),
14028 errdetail("%s depends on column \"%s\"",
14029 getObjectDescription(&foundObject
, false),
14033 case AttrDefaultRelationId
:
14035 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
14037 if (col
.objectId
== RelationGetRelid(rel
) &&
14038 col
.objectSubId
== attnum
)
14041 * Ignore the column's own default expression. The
14042 * caller deals with it.
14048 * This must be a reference from the expression of a
14049 * generated column elsewhere in the same table.
14050 * Changing the type/generated expression of a column
14051 * that is used by a generated column is not allowed
14052 * by SQL standard, so just punt for now. It might be
14053 * doable with some thinking and effort.
14055 if (subtype
== AT_AlterColumnType
)
14057 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14058 errmsg("cannot alter type of a column used by a generated column"),
14059 errdetail("Column \"%s\" is used by generated column \"%s\".",
14061 get_attname(col
.objectId
,
14068 case StatisticExtRelationId
:
14071 * Give the extended-stats machinery a chance to fix anything
14072 * that this column type change would break.
14074 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
14077 case PublicationRelRelationId
:
14080 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14081 * clause. Same issues as above. FIXME someday.
14083 if (subtype
== AT_AlterColumnType
)
14085 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14086 errmsg("cannot alter type of a column used by a publication WHERE clause"),
14087 errdetail("%s depends on column \"%s\"",
14088 getObjectDescription(&foundObject
, false),
14095 * We don't expect any other sorts of objects to depend on a
14098 elog(ERROR
, "unexpected object depending on column: %s",
14099 getObjectDescription(&foundObject
, false));
14104 systable_endscan(scan
);
14105 table_close(depRel
, NoLock
);
14109 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14110 * needs to be reset.
14113 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14115 if (!get_index_isreplident(indoid
))
14118 if (tab
->replicaIdentityIndex
)
14119 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
14121 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
14125 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14128 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14130 if (!get_index_isclustered(indoid
))
14133 if (tab
->clusterOnIndex
)
14134 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
14136 tab
->clusterOnIndex
= get_rel_name(indoid
);
14140 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14141 * to be rebuilt (which we might already know).
14144 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
14147 * This de-duplication check is critical for two independent reasons: we
14148 * mustn't try to recreate the same constraint twice, and if a constraint
14149 * depends on more than one column whose type is to be altered, we must
14150 * capture its definition string before applying any of the column type
14151 * changes. ruleutils.c will get confused if we ask again later.
14153 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
14155 /* OK, capture the constraint's existing definition string */
14156 char *defstring
= pg_get_constraintdef_command(conoid
);
14160 * It is critical to create not-null constraints ahead of primary key
14161 * indexes; otherwise, the not-null constraint would be created by the
14162 * primary key, and the constraint name would be wrong.
14164 if (get_constraint_type(conoid
) == CONSTRAINT_NOTNULL
)
14166 tab
->changedConstraintOids
= lcons_oid(conoid
,
14167 tab
->changedConstraintOids
);
14168 tab
->changedConstraintDefs
= lcons(defstring
,
14169 tab
->changedConstraintDefs
);
14174 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
14176 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
14181 * For the index of a constraint, if any, remember if it is used for
14182 * the table's replica identity or if it is a clustered index, so that
14183 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14184 * those properties.
14186 indoid
= get_constraint_index(conoid
);
14187 if (OidIsValid(indoid
))
14189 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14190 RememberClusterOnForRebuilding(indoid
, tab
);
14196 * Subroutine for ATExecAlterColumnType: remember that an index needs
14197 * to be rebuilt (which we might already know).
14200 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14203 * This de-duplication check is critical for two independent reasons: we
14204 * mustn't try to recreate the same index twice, and if an index depends
14205 * on more than one column whose type is to be altered, we must capture
14206 * its definition string before applying any of the column type changes.
14207 * ruleutils.c will get confused if we ask again later.
14209 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
14212 * Before adding it as an index-to-rebuild, we'd better see if it
14213 * belongs to a constraint, and if so rebuild the constraint instead.
14214 * Typically this check fails, because constraint indexes normally
14215 * have only dependencies on their constraint. But it's possible for
14216 * such an index to also have direct dependencies on table columns,
14217 * for example with a partial exclusion constraint.
14219 Oid conoid
= get_index_constraint(indoid
);
14221 if (OidIsValid(conoid
))
14223 RememberConstraintForRebuilding(conoid
, tab
);
14227 /* OK, capture the index's existing definition string */
14228 char *defstring
= pg_get_indexdef_string(indoid
);
14230 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
14232 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
14236 * Remember if this index is used for the table's replica identity
14237 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14238 * can queue up commands necessary to restore those properties.
14240 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14241 RememberClusterOnForRebuilding(indoid
, tab
);
14247 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14248 * needs to be rebuilt (which we might already know).
14251 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
14254 * This de-duplication check is critical for two independent reasons: we
14255 * mustn't try to recreate the same statistics object twice, and if the
14256 * statistics object depends on more than one column whose type is to be
14257 * altered, we must capture its definition string before applying any of
14258 * the type changes. ruleutils.c will get confused if we ask again later.
14260 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
14262 /* OK, capture the statistics object's existing definition string */
14263 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
14265 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
14267 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
14273 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14274 * operations for a particular relation. We have to drop and recreate all the
14275 * indexes and constraints that depend on the altered columns. We do the
14276 * actual dropping here, but re-creation is managed by adding work queue
14277 * entries to do those steps later.
14280 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
14283 ObjectAddresses
*objects
;
14284 ListCell
*def_item
;
14285 ListCell
*oid_item
;
14288 * Collect all the constraints and indexes to drop so we can process them
14289 * in a single call. That way we don't have to worry about dependencies
14292 objects
= new_object_addresses();
14295 * Re-parse the index and constraint definitions, and attach them to the
14296 * appropriate work queue entries. We do this before dropping because in
14297 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14298 * lock on the table the constraint is attached to, and we need to get
14299 * that before reparsing/dropping.
14301 * We can't rely on the output of deparsing to tell us which relation to
14302 * operate on, because concurrent activity might have made the name
14303 * resolve differently. Instead, we've got to use the OID of the
14304 * constraint or index we're processing to figure out which relation to
14307 forboth(oid_item
, tab
->changedConstraintOids
,
14308 def_item
, tab
->changedConstraintDefs
)
14310 Oid oldId
= lfirst_oid(oid_item
);
14312 Form_pg_constraint con
;
14318 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14319 if (!HeapTupleIsValid(tup
)) /* should not happen */
14320 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14321 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
14322 if (OidIsValid(con
->conrelid
))
14323 relid
= con
->conrelid
;
14326 /* must be a domain constraint */
14327 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
14328 if (!OidIsValid(relid
))
14329 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
14331 confrelid
= con
->confrelid
;
14332 contype
= con
->contype
;
14333 conislocal
= con
->conislocal
;
14334 ReleaseSysCache(tup
);
14336 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
14337 add_exact_object_address(&obj
, objects
);
14340 * If the constraint is inherited (only), we don't want to inject a
14341 * new definition here; it'll get recreated when
14342 * ATAddCheckNNConstraint recurses from adding the parent table's
14343 * constraint. But we had to carry the info this far so that we can
14344 * drop the constraint below.
14350 * When rebuilding an FK constraint that references the table we're
14351 * modifying, we might not yet have any lock on the FK's table, so get
14352 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14353 * step, so there's no value in asking for anything weaker.
14355 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
14356 LockRelationOid(relid
, AccessExclusiveLock
);
14358 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
14359 (char *) lfirst(def_item
),
14360 wqueue
, lockmode
, tab
->rewrite
);
14362 forboth(oid_item
, tab
->changedIndexOids
,
14363 def_item
, tab
->changedIndexDefs
)
14365 Oid oldId
= lfirst_oid(oid_item
);
14368 relid
= IndexGetRelation(oldId
, false);
14369 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14370 (char *) lfirst(def_item
),
14371 wqueue
, lockmode
, tab
->rewrite
);
14373 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
14374 add_exact_object_address(&obj
, objects
);
14377 /* add dependencies for new statistics */
14378 forboth(oid_item
, tab
->changedStatisticsOids
,
14379 def_item
, tab
->changedStatisticsDefs
)
14381 Oid oldId
= lfirst_oid(oid_item
);
14384 relid
= StatisticsGetRelation(oldId
, false);
14385 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14386 (char *) lfirst(def_item
),
14387 wqueue
, lockmode
, tab
->rewrite
);
14389 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
14390 add_exact_object_address(&obj
, objects
);
14394 * Queue up command to restore replica identity index marking
14396 if (tab
->replicaIdentityIndex
)
14398 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14399 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
14401 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
14402 subcmd
->name
= tab
->replicaIdentityIndex
;
14403 cmd
->subtype
= AT_ReplicaIdentity
;
14404 cmd
->def
= (Node
*) subcmd
;
14406 /* do it after indexes and constraints */
14407 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14408 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14412 * Queue up command to restore marking of index used for cluster.
14414 if (tab
->clusterOnIndex
)
14416 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14418 cmd
->subtype
= AT_ClusterOn
;
14419 cmd
->name
= tab
->clusterOnIndex
;
14421 /* do it after indexes and constraints */
14422 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14423 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14427 * It should be okay to use DROP_RESTRICT here, since nothing else should
14428 * be depending on these objects.
14430 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
14432 free_object_addresses(objects
);
14435 * The objects will get recreated during subsequent passes over the work
14441 * Parse the previously-saved definition string for a constraint, index or
14442 * statistics object against the newly-established column data type(s), and
14443 * queue up the resulting command parsetrees for execution.
14445 * This might fail if, for example, you have a WHERE clause that uses an
14446 * operator that's not available for the new column type.
14449 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
14450 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
14452 List
*raw_parsetree_list
;
14453 List
*querytree_list
;
14454 ListCell
*list_item
;
14458 * We expect that we will get only ALTER TABLE and CREATE INDEX
14459 * statements. Hence, there is no need to pass them through
14460 * parse_analyze_*() or the rewriter, but instead we need to pass them
14461 * through parse_utilcmd.c to make them ready for execution.
14463 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
14464 querytree_list
= NIL
;
14465 foreach(list_item
, raw_parsetree_list
)
14467 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
14468 Node
*stmt
= rs
->stmt
;
14470 if (IsA(stmt
, IndexStmt
))
14471 querytree_list
= lappend(querytree_list
,
14472 transformIndexStmt(oldRelId
,
14473 (IndexStmt
*) stmt
,
14475 else if (IsA(stmt
, AlterTableStmt
))
14480 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
14481 (AlterTableStmt
*) stmt
,
14485 querytree_list
= list_concat(querytree_list
, beforeStmts
);
14486 querytree_list
= lappend(querytree_list
, stmt
);
14487 querytree_list
= list_concat(querytree_list
, afterStmts
);
14489 else if (IsA(stmt
, CreateStatsStmt
))
14490 querytree_list
= lappend(querytree_list
,
14491 transformStatsStmt(oldRelId
,
14492 (CreateStatsStmt
*) stmt
,
14495 querytree_list
= lappend(querytree_list
, stmt
);
14498 /* Caller should already have acquired whatever lock we need. */
14499 rel
= relation_open(oldRelId
, NoLock
);
14502 * Attach each generated command to the proper place in the work queue.
14503 * Note this could result in creation of entirely new work-queue entries.
14505 * Also note that we have to tweak the command subtypes, because it turns
14506 * out that re-creation of indexes and constraints has to act a bit
14507 * differently from initial creation.
14509 foreach(list_item
, querytree_list
)
14511 Node
*stm
= (Node
*) lfirst(list_item
);
14512 AlteredTableInfo
*tab
;
14514 tab
= ATGetQueueEntry(wqueue
, rel
);
14516 if (IsA(stm
, IndexStmt
))
14518 IndexStmt
*stmt
= (IndexStmt
*) stm
;
14519 AlterTableCmd
*newcmd
;
14522 TryReuseIndex(oldId
, stmt
);
14523 stmt
->reset_default_tblspc
= true;
14524 /* keep the index's comment */
14525 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
14527 newcmd
= makeNode(AlterTableCmd
);
14528 newcmd
->subtype
= AT_ReAddIndex
;
14529 newcmd
->def
= (Node
*) stmt
;
14530 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14531 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
14533 else if (IsA(stm
, AlterTableStmt
))
14535 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
14538 foreach(lcmd
, stmt
->cmds
)
14540 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
14542 if (cmd
->subtype
== AT_AddIndex
)
14544 IndexStmt
*indstmt
;
14547 indstmt
= castNode(IndexStmt
, cmd
->def
);
14548 indoid
= get_constraint_index(oldId
);
14551 TryReuseIndex(indoid
, indstmt
);
14552 /* keep any comment on the index */
14553 indstmt
->idxcomment
= GetComment(indoid
,
14554 RelationRelationId
, 0);
14555 indstmt
->reset_default_tblspc
= true;
14557 cmd
->subtype
= AT_ReAddIndex
;
14558 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14559 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
14561 /* recreate any comment on the constraint */
14562 RebuildConstraintComment(tab
,
14569 else if (cmd
->subtype
== AT_AddConstraint
)
14571 Constraint
*con
= castNode(Constraint
, cmd
->def
);
14573 con
->old_pktable_oid
= refRelId
;
14574 /* rewriting neither side of a FK */
14575 if (con
->contype
== CONSTR_FOREIGN
&&
14576 !rewrite
&& tab
->rewrite
== 0)
14577 TryReuseForeignKey(oldId
, con
);
14578 con
->reset_default_tblspc
= true;
14579 cmd
->subtype
= AT_ReAddConstraint
;
14580 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14581 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14584 * Recreate any comment on the constraint. If we have
14585 * recreated a primary key, then transformTableConstraint
14586 * has added an unnamed not-null constraint here; skip
14587 * this in that case.
14590 RebuildConstraintComment(tab
,
14591 AT_PASS_OLD_CONSTR
,
14597 Assert(con
->contype
== CONSTR_NOTNULL
);
14600 elog(ERROR
, "unexpected statement subtype: %d",
14601 (int) cmd
->subtype
);
14604 else if (IsA(stm
, AlterDomainStmt
))
14606 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
14608 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
14610 Constraint
*con
= castNode(Constraint
, stmt
->def
);
14611 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14613 cmd
->subtype
= AT_ReAddDomainConstraint
;
14614 cmd
->def
= (Node
*) stmt
;
14615 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14616 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14618 /* recreate any comment on the constraint */
14619 RebuildConstraintComment(tab
,
14620 AT_PASS_OLD_CONSTR
,
14627 elog(ERROR
, "unexpected statement subtype: %d",
14628 (int) stmt
->subtype
);
14630 else if (IsA(stm
, CreateStatsStmt
))
14632 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
14633 AlterTableCmd
*newcmd
;
14635 /* keep the statistics object's comment */
14636 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
14638 newcmd
= makeNode(AlterTableCmd
);
14639 newcmd
->subtype
= AT_ReAddStatistics
;
14640 newcmd
->def
= (Node
*) stmt
;
14641 tab
->subcmds
[AT_PASS_MISC
] =
14642 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
14645 elog(ERROR
, "unexpected statement type: %d",
14646 (int) nodeTag(stm
));
14649 relation_close(rel
, NoLock
);
14653 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14654 * for a table or domain constraint that is being rebuilt.
14656 * objid is the OID of the constraint.
14657 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14658 * as a string list) for a domain constraint.
14659 * (We could dig that info, as well as the conname, out of the pg_constraint
14660 * entry; but callers already have them so might as well pass them.)
14663 RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
, Oid objid
,
14664 Relation rel
, List
*domname
,
14665 const char *conname
)
14669 AlterTableCmd
*newcmd
;
14671 /* Look for comment for object wanted, and leave if none */
14672 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
14673 if (comment_str
== NULL
)
14676 /* Build CommentStmt node, copying all input data for safety */
14677 cmd
= makeNode(CommentStmt
);
14680 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
14681 cmd
->object
= (Node
*)
14682 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
14683 makeString(pstrdup(RelationGetRelationName(rel
))),
14684 makeString(pstrdup(conname
)));
14688 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
14689 cmd
->object
= (Node
*)
14690 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
14691 makeString(pstrdup(conname
)));
14693 cmd
->comment
= comment_str
;
14695 /* Append it to list of commands */
14696 newcmd
= makeNode(AlterTableCmd
);
14697 newcmd
->subtype
= AT_ReAddComment
;
14698 newcmd
->def
= (Node
*) cmd
;
14699 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
14703 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14704 * for the real analysis, then mutates the IndexStmt based on that verdict.
14707 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
14709 if (CheckIndexCompatible(oldId
,
14710 stmt
->accessMethod
,
14712 stmt
->excludeOpNames
,
14713 stmt
->iswithoutoverlaps
))
14715 Relation irel
= index_open(oldId
, NoLock
);
14717 /* If it's a partitioned index, there is no storage to share. */
14718 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
14720 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
14721 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
14722 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
14724 index_close(irel
, NoLock
);
14729 * Subroutine for ATPostAlterTypeParse().
14731 * Stash the old P-F equality operator into the Constraint node, for possible
14732 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14733 * this constraint can be skipped.
14736 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
14745 Assert(con
->contype
== CONSTR_FOREIGN
);
14746 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
14748 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14749 if (!HeapTupleIsValid(tup
)) /* should not happen */
14750 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14752 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
14753 Anum_pg_constraint_conpfeqop
);
14754 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
14755 numkeys
= ARR_DIMS(arr
)[0];
14756 /* test follows the one in ri_FetchConstraintInfo() */
14757 if (ARR_NDIM(arr
) != 1 ||
14758 ARR_HASNULL(arr
) ||
14759 ARR_ELEMTYPE(arr
) != OIDOID
)
14760 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
14761 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
14763 /* stash a List of the operator Oids in our Constraint node */
14764 for (i
= 0; i
< numkeys
; i
++)
14765 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
14767 ReleaseSysCache(tup
);
14771 * ALTER COLUMN .. OPTIONS ( ... )
14773 * Returns the address of the modified column
14775 static ObjectAddress
14776 ATExecAlterColumnGenericOptions(Relation rel
,
14777 const char *colName
,
14783 ForeignServer
*server
;
14784 ForeignDataWrapper
*fdw
;
14786 HeapTuple newtuple
;
14788 Datum repl_val
[Natts_pg_attribute
];
14789 bool repl_null
[Natts_pg_attribute
];
14790 bool repl_repl
[Natts_pg_attribute
];
14792 Form_pg_foreign_table fttableform
;
14793 Form_pg_attribute atttableform
;
14795 ObjectAddress address
;
14797 if (options
== NIL
)
14798 return InvalidObjectAddress
;
14800 /* First, determine FDW validator associated to the foreign table. */
14801 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
14802 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
14803 if (!HeapTupleIsValid(tuple
))
14805 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14806 errmsg("foreign table \"%s\" does not exist",
14807 RelationGetRelationName(rel
))));
14808 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
14809 server
= GetForeignServer(fttableform
->ftserver
);
14810 fdw
= GetForeignDataWrapper(server
->fdwid
);
14812 table_close(ftrel
, AccessShareLock
);
14813 ReleaseSysCache(tuple
);
14815 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
14816 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
14817 if (!HeapTupleIsValid(tuple
))
14819 (errcode(ERRCODE_UNDEFINED_COLUMN
),
14820 errmsg("column \"%s\" of relation \"%s\" does not exist",
14821 colName
, RelationGetRelationName(rel
))));
14823 /* Prevent them from altering a system attribute */
14824 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
14825 attnum
= atttableform
->attnum
;
14828 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14829 errmsg("cannot alter system column \"%s\"", colName
)));
14832 /* Initialize buffers for new tuple values */
14833 memset(repl_val
, 0, sizeof(repl_val
));
14834 memset(repl_null
, false, sizeof(repl_null
));
14835 memset(repl_repl
, false, sizeof(repl_repl
));
14837 /* Extract the current options */
14838 datum
= SysCacheGetAttr(ATTNAME
,
14840 Anum_pg_attribute_attfdwoptions
,
14843 datum
= PointerGetDatum(NULL
);
14845 /* Transform the options */
14846 datum
= transformGenericOptions(AttributeRelationId
,
14849 fdw
->fdwvalidator
);
14851 if (PointerIsValid(DatumGetPointer(datum
)))
14852 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
14854 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14856 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14858 /* Everything looks good - update the tuple */
14860 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
14861 repl_val
, repl_null
, repl_repl
);
14863 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
14865 InvokeObjectPostAlterHook(RelationRelationId
,
14866 RelationGetRelid(rel
),
14867 atttableform
->attnum
);
14868 ObjectAddressSubSet(address
, RelationRelationId
,
14869 RelationGetRelid(rel
), attnum
);
14871 ReleaseSysCache(tuple
);
14873 table_close(attrel
, RowExclusiveLock
);
14875 heap_freetuple(newtuple
);
14881 * ALTER TABLE OWNER
14883 * recursing is true if we are recursing from a table to its indexes,
14884 * sequences, or toast table. We don't allow the ownership of those things to
14885 * be changed separately from the parent table. Also, we can skip permission
14886 * checks (this is necessary not just an optimization, else we'd fail to
14887 * handle toast tables properly).
14889 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14890 * free-standing composite type.
14893 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
14895 Relation target_rel
;
14896 Relation class_rel
;
14898 Form_pg_class tuple_class
;
14901 * Get exclusive lock till end of transaction on the target table. Use
14902 * relation_open so that we can work on indexes and sequences.
14904 target_rel
= relation_open(relationOid
, lockmode
);
14906 /* Get its pg_class tuple, too */
14907 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
14909 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
14910 if (!HeapTupleIsValid(tuple
))
14911 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
14912 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
14914 /* Can we change the ownership of this tuple? */
14915 switch (tuple_class
->relkind
)
14917 case RELKIND_RELATION
:
14919 case RELKIND_MATVIEW
:
14920 case RELKIND_FOREIGN_TABLE
:
14921 case RELKIND_PARTITIONED_TABLE
:
14922 /* ok to change owner */
14924 case RELKIND_INDEX
:
14928 * Because ALTER INDEX OWNER used to be allowed, and in fact
14929 * is generated by old versions of pg_dump, we give a warning
14930 * and do nothing rather than erroring out. Also, to avoid
14931 * unnecessary chatter while restoring those old dumps, say
14932 * nothing at all if the command would be a no-op anyway.
14934 if (tuple_class
->relowner
!= newOwnerId
)
14936 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14937 errmsg("cannot change owner of index \"%s\"",
14938 NameStr(tuple_class
->relname
)),
14939 errhint("Change the ownership of the index's table instead.")));
14940 /* quick hack to exit via the no-op path */
14941 newOwnerId
= tuple_class
->relowner
;
14944 case RELKIND_PARTITIONED_INDEX
:
14948 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14949 errmsg("cannot change owner of index \"%s\"",
14950 NameStr(tuple_class
->relname
)),
14951 errhint("Change the ownership of the index's table instead.")));
14953 case RELKIND_SEQUENCE
:
14955 tuple_class
->relowner
!= newOwnerId
)
14957 /* if it's an owned sequence, disallow changing it by itself */
14961 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
14962 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
14964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14965 errmsg("cannot change owner of sequence \"%s\"",
14966 NameStr(tuple_class
->relname
)),
14967 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14968 NameStr(tuple_class
->relname
),
14969 get_rel_name(tableId
))));
14972 case RELKIND_COMPOSITE_TYPE
:
14976 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14977 errmsg("\"%s\" is a composite type",
14978 NameStr(tuple_class
->relname
)),
14979 /* translator: %s is an SQL ALTER command */
14980 errhint("Use %s instead.",
14983 case RELKIND_TOASTVALUE
:
14989 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14990 errmsg("cannot change owner of relation \"%s\"",
14991 NameStr(tuple_class
->relname
)),
14992 errdetail_relkind_not_supported(tuple_class
->relkind
)));
14996 * If the new owner is the same as the existing owner, consider the
14997 * command to have succeeded. This is for dump restoration purposes.
14999 if (tuple_class
->relowner
!= newOwnerId
)
15001 Datum repl_val
[Natts_pg_class
];
15002 bool repl_null
[Natts_pg_class
];
15003 bool repl_repl
[Natts_pg_class
];
15007 HeapTuple newtuple
;
15009 /* skip permission checks when recursing to index or toast table */
15012 /* Superusers can always do it */
15015 Oid namespaceOid
= tuple_class
->relnamespace
;
15016 AclResult aclresult
;
15018 /* Otherwise, must be owner of the existing object */
15019 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
15020 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
15021 RelationGetRelationName(target_rel
));
15023 /* Must be able to become new owner */
15024 check_can_set_role(GetUserId(), newOwnerId
);
15026 /* New owner must have CREATE privilege on namespace */
15027 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
15029 if (aclresult
!= ACLCHECK_OK
)
15030 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
15031 get_namespace_name(namespaceOid
));
15035 memset(repl_null
, false, sizeof(repl_null
));
15036 memset(repl_repl
, false, sizeof(repl_repl
));
15038 repl_repl
[Anum_pg_class_relowner
- 1] = true;
15039 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
15042 * Determine the modified ACL for the new owner. This is only
15043 * necessary when the ACL is non-null.
15045 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
15046 Anum_pg_class_relacl
,
15050 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15051 tuple_class
->relowner
, newOwnerId
);
15052 repl_repl
[Anum_pg_class_relacl
- 1] = true;
15053 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
15056 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
15058 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
15060 heap_freetuple(newtuple
);
15063 * We must similarly update any per-column ACLs to reflect the new
15064 * owner; for neatness reasons that's split out as a subroutine.
15066 change_owner_fix_column_acls(relationOid
,
15067 tuple_class
->relowner
,
15071 * Update owner dependency reference, if any. A composite type has
15072 * none, because it's tracked for the pg_type entry instead of here;
15073 * indexes and TOAST tables don't have their own entries either.
15075 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
15076 tuple_class
->relkind
!= RELKIND_INDEX
&&
15077 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
15078 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
15079 changeDependencyOnOwner(RelationRelationId
, relationOid
,
15083 * Also change the ownership of the table's row type, if it has one
15085 if (OidIsValid(tuple_class
->reltype
))
15086 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
15089 * If we are operating on a table or materialized view, also change
15090 * the ownership of any indexes and sequences that belong to the
15091 * relation, as well as its toast table (if it has one).
15093 if (tuple_class
->relkind
== RELKIND_RELATION
||
15094 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
15095 tuple_class
->relkind
== RELKIND_MATVIEW
||
15096 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
15098 List
*index_oid_list
;
15101 /* Find all the indexes belonging to this relation */
15102 index_oid_list
= RelationGetIndexList(target_rel
);
15104 /* For each index, recursively change its ownership */
15105 foreach(i
, index_oid_list
)
15106 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
15108 list_free(index_oid_list
);
15111 /* If it has a toast table, recurse to change its ownership */
15112 if (tuple_class
->reltoastrelid
!= InvalidOid
)
15113 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
15116 /* If it has dependent sequences, recurse to change them too */
15117 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
15120 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
15122 ReleaseSysCache(tuple
);
15123 table_close(class_rel
, RowExclusiveLock
);
15124 relation_close(target_rel
, NoLock
);
15128 * change_owner_fix_column_acls
15130 * Helper function for ATExecChangeOwner. Scan the columns of the table
15131 * and fix any non-null column ACLs to reflect the new owner.
15134 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
15136 Relation attRelation
;
15138 ScanKeyData key
[1];
15139 HeapTuple attributeTuple
;
15141 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
15142 ScanKeyInit(&key
[0],
15143 Anum_pg_attribute_attrelid
,
15144 BTEqualStrategyNumber
, F_OIDEQ
,
15145 ObjectIdGetDatum(relationOid
));
15146 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
15147 true, NULL
, 1, key
);
15148 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
15150 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
15151 Datum repl_val
[Natts_pg_attribute
];
15152 bool repl_null
[Natts_pg_attribute
];
15153 bool repl_repl
[Natts_pg_attribute
];
15157 HeapTuple newtuple
;
15159 /* Ignore dropped columns */
15160 if (att
->attisdropped
)
15163 aclDatum
= heap_getattr(attributeTuple
,
15164 Anum_pg_attribute_attacl
,
15165 RelationGetDescr(attRelation
),
15167 /* Null ACLs do not require changes */
15171 memset(repl_null
, false, sizeof(repl_null
));
15172 memset(repl_repl
, false, sizeof(repl_repl
));
15174 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15175 oldOwnerId
, newOwnerId
);
15176 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
15177 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
15179 newtuple
= heap_modify_tuple(attributeTuple
,
15180 RelationGetDescr(attRelation
),
15181 repl_val
, repl_null
, repl_repl
);
15183 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
15185 heap_freetuple(newtuple
);
15187 systable_endscan(scan
);
15188 table_close(attRelation
, RowExclusiveLock
);
15192 * change_owner_recurse_to_sequences
15194 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15195 * for sequences that are dependent on serial columns, and changes their
15199 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
15203 ScanKeyData key
[2];
15207 * SERIAL sequences are those having an auto dependency on one of the
15208 * table's columns (we don't care *which* column, exactly).
15210 depRel
= table_open(DependRelationId
, AccessShareLock
);
15212 ScanKeyInit(&key
[0],
15213 Anum_pg_depend_refclassid
,
15214 BTEqualStrategyNumber
, F_OIDEQ
,
15215 ObjectIdGetDatum(RelationRelationId
));
15216 ScanKeyInit(&key
[1],
15217 Anum_pg_depend_refobjid
,
15218 BTEqualStrategyNumber
, F_OIDEQ
,
15219 ObjectIdGetDatum(relationOid
));
15220 /* we leave refobjsubid unspecified */
15222 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
15225 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
15227 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
15230 /* skip dependencies other than auto dependencies on columns */
15231 if (depForm
->refobjsubid
== 0 ||
15232 depForm
->classid
!= RelationRelationId
||
15233 depForm
->objsubid
!= 0 ||
15234 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
15237 /* Use relation_open just in case it's an index */
15238 seqRel
= relation_open(depForm
->objid
, lockmode
);
15240 /* skip non-sequence relations */
15241 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
15243 /* No need to keep the lock */
15244 relation_close(seqRel
, lockmode
);
15248 /* We don't need to close the sequence while we alter it. */
15249 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
15251 /* Now we can close it. Keep the lock till end of transaction. */
15252 relation_close(seqRel
, NoLock
);
15255 systable_endscan(scan
);
15257 relation_close(depRel
, AccessShareLock
);
15261 * ALTER TABLE CLUSTER ON
15263 * The only thing we have to do is to change the indisclustered bits.
15265 * Return the address of the new clustering index.
15267 static ObjectAddress
15268 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
15271 ObjectAddress address
;
15273 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
15275 if (!OidIsValid(indexOid
))
15277 (errcode(ERRCODE_UNDEFINED_OBJECT
),
15278 errmsg("index \"%s\" for table \"%s\" does not exist",
15279 indexName
, RelationGetRelationName(rel
))));
15281 /* Check index is valid to cluster on */
15282 check_index_is_clusterable(rel
, indexOid
, lockmode
);
15284 /* And do the work */
15285 mark_index_clustered(rel
, indexOid
, false);
15287 ObjectAddressSet(address
,
15288 RelationRelationId
, indexOid
);
15294 * ALTER TABLE SET WITHOUT CLUSTER
15296 * We have to find any indexes on the table that have indisclustered bit
15297 * set and turn it off.
15300 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
15302 mark_index_clustered(rel
, InvalidOid
, false);
15306 * Preparation phase for SET ACCESS METHOD
15308 * Check that the access method exists and determine whether a change is
15312 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
15317 * Look up the access method name and check that it differs from the
15318 * table's current AM. If DEFAULT was specified for a partitioned table
15319 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15321 if (amname
!= NULL
)
15322 amoid
= get_table_am_oid(amname
, false);
15323 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15324 amoid
= InvalidOid
;
15326 amoid
= get_table_am_oid(default_table_access_method
, false);
15328 /* if it's a match, phase 3 doesn't need to do anything */
15329 if (rel
->rd_rel
->relam
== amoid
)
15332 /* Save info for Phase 3 to do the real work */
15333 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
15334 tab
->newAccessMethod
= amoid
;
15335 tab
->chgAccessMethod
= true;
15339 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15340 * storage that have an interest in preserving AM.
15342 * Since these have no storage, setting the access method is a catalog only
15346 ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
)
15349 Oid oldAccessMethodId
;
15351 Form_pg_class rd_rel
;
15352 Oid reloid
= RelationGetRelid(rel
);
15355 * Shouldn't be called on relations having storage; these are processed in
15358 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15360 /* Get a modifiable copy of the relation's pg_class row. */
15361 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
15363 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
15364 if (!HeapTupleIsValid(tuple
))
15365 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
15366 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
15368 /* Update the pg_class row. */
15369 oldAccessMethodId
= rd_rel
->relam
;
15370 rd_rel
->relam
= newAccessMethodId
;
15372 /* Leave if no update required */
15373 if (rd_rel
->relam
== oldAccessMethodId
)
15375 heap_freetuple(tuple
);
15376 table_close(pg_class
, RowExclusiveLock
);
15380 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
15383 * Update the dependency on the new access method. No dependency is added
15384 * if the new access method is InvalidOid (default case). Be very careful
15385 * that this has to compare the previous value stored in pg_class with the
15388 if (!OidIsValid(oldAccessMethodId
) && OidIsValid(rd_rel
->relam
))
15390 ObjectAddress relobj
,
15394 * New access method is defined and there was no dependency
15395 * previously, so record a new one.
15397 ObjectAddressSet(relobj
, RelationRelationId
, reloid
);
15398 ObjectAddressSet(referenced
, AccessMethodRelationId
, rd_rel
->relam
);
15399 recordDependencyOn(&relobj
, &referenced
, DEPENDENCY_NORMAL
);
15401 else if (OidIsValid(oldAccessMethodId
) &&
15402 !OidIsValid(rd_rel
->relam
))
15405 * There was an access method defined, and no new one, so just remove
15406 * the existing dependency.
15408 deleteDependencyRecordsForClass(RelationRelationId
, reloid
,
15409 AccessMethodRelationId
,
15410 DEPENDENCY_NORMAL
);
15414 Assert(OidIsValid(oldAccessMethodId
) &&
15415 OidIsValid(rd_rel
->relam
));
15417 /* Both are valid, so update the dependency */
15418 changeDependencyFor(RelationRelationId
, reloid
,
15419 AccessMethodRelationId
,
15420 oldAccessMethodId
, rd_rel
->relam
);
15423 /* make the relam and dependency changes visible */
15424 CommandCounterIncrement();
15426 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15428 heap_freetuple(tuple
);
15429 table_close(pg_class
, RowExclusiveLock
);
15433 * ALTER TABLE SET TABLESPACE
15436 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
15440 /* Check that the tablespace exists */
15441 tablespaceId
= get_tablespace_oid(tablespacename
, false);
15443 /* Check permissions except when moving to database's default */
15444 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
15446 AclResult aclresult
;
15448 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
15449 if (aclresult
!= ACLCHECK_OK
)
15450 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
15453 /* Save info for Phase 3 to do the real work */
15454 if (OidIsValid(tab
->newTableSpace
))
15456 (errcode(ERRCODE_SYNTAX_ERROR
),
15457 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15459 tab
->newTableSpace
= tablespaceId
;
15463 * Set, reset, or replace reloptions.
15466 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
15472 HeapTuple newtuple
;
15476 Datum repl_val
[Natts_pg_class
];
15477 bool repl_null
[Natts_pg_class
];
15478 bool repl_repl
[Natts_pg_class
];
15479 const char *const validnsps
[] = HEAP_RELOPT_NAMESPACES
;
15481 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
15482 return; /* nothing to do */
15484 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
15486 /* Fetch heap tuple */
15487 relid
= RelationGetRelid(rel
);
15488 tuple
= SearchSysCacheLocked1(RELOID
, ObjectIdGetDatum(relid
));
15489 if (!HeapTupleIsValid(tuple
))
15490 elog(ERROR
, "cache lookup failed for relation %u", relid
);
15492 if (operation
== AT_ReplaceRelOptions
)
15495 * If we're supposed to replace the reloptions list, we just pretend
15496 * there were none before.
15503 /* Get the old reloptions */
15504 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15508 /* Generate new proposed reloptions (text array) */
15509 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15510 defList
, NULL
, validnsps
, false,
15511 operation
== AT_ResetRelOptions
);
15514 switch (rel
->rd_rel
->relkind
)
15516 case RELKIND_RELATION
:
15517 case RELKIND_TOASTVALUE
:
15518 case RELKIND_MATVIEW
:
15519 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
15521 case RELKIND_PARTITIONED_TABLE
:
15522 (void) partitioned_table_reloptions(newOptions
, true);
15525 (void) view_reloptions(newOptions
, true);
15527 case RELKIND_INDEX
:
15528 case RELKIND_PARTITIONED_INDEX
:
15529 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
15533 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15534 errmsg("cannot set options for relation \"%s\"",
15535 RelationGetRelationName(rel
)),
15536 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
15540 /* Special-case validation of view options */
15541 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
15543 Query
*view_query
= get_view_query(rel
);
15544 List
*view_options
= untransformRelOptions(newOptions
);
15546 bool check_option
= false;
15548 foreach(cell
, view_options
)
15550 DefElem
*defel
= (DefElem
*) lfirst(cell
);
15552 if (strcmp(defel
->defname
, "check_option") == 0)
15553 check_option
= true;
15557 * If the check option is specified, look to see if the view is
15558 * actually auto-updatable or not.
15562 const char *view_updatable_error
=
15563 view_query_is_auto_updatable(view_query
, true);
15565 if (view_updatable_error
)
15567 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15568 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15569 errhint("%s", _(view_updatable_error
))));
15574 * All we need do here is update the pg_class row; the new options will be
15575 * propagated into relcaches during post-commit cache inval.
15577 memset(repl_val
, 0, sizeof(repl_val
));
15578 memset(repl_null
, false, sizeof(repl_null
));
15579 memset(repl_repl
, false, sizeof(repl_repl
));
15581 if (newOptions
!= (Datum
) 0)
15582 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15584 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15586 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15588 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15589 repl_val
, repl_null
, repl_repl
);
15591 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15592 UnlockTuple(pgclass
, &tuple
->t_self
, InplaceUpdateTupleLock
);
15594 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15596 heap_freetuple(newtuple
);
15598 ReleaseSysCache(tuple
);
15600 /* repeat the whole exercise for the toast table, if there's one */
15601 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
15604 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
15606 toastrel
= table_open(toastid
, lockmode
);
15608 /* Fetch heap tuple */
15609 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
15610 if (!HeapTupleIsValid(tuple
))
15611 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
15613 if (operation
== AT_ReplaceRelOptions
)
15616 * If we're supposed to replace the reloptions list, we just
15617 * pretend there were none before.
15624 /* Get the old reloptions */
15625 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15629 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15630 defList
, "toast", validnsps
, false,
15631 operation
== AT_ResetRelOptions
);
15633 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
15635 memset(repl_val
, 0, sizeof(repl_val
));
15636 memset(repl_null
, false, sizeof(repl_null
));
15637 memset(repl_repl
, false, sizeof(repl_repl
));
15639 if (newOptions
!= (Datum
) 0)
15640 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15642 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15644 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15646 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15647 repl_val
, repl_null
, repl_repl
);
15649 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15651 InvokeObjectPostAlterHookArg(RelationRelationId
,
15652 RelationGetRelid(toastrel
), 0,
15655 heap_freetuple(newtuple
);
15657 ReleaseSysCache(tuple
);
15659 table_close(toastrel
, NoLock
);
15662 table_close(pgclass
, RowExclusiveLock
);
15666 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15667 * rewriting to be done, so we just want to copy the data as fast as possible.
15670 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
15674 RelFileNumber newrelfilenumber
;
15675 RelFileLocator newrlocator
;
15676 List
*reltoastidxids
= NIL
;
15680 * Need lock here in case we are recursing to toast table or index
15682 rel
= relation_open(tableOid
, lockmode
);
15684 /* Check first if relation can be moved to new tablespace */
15685 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15687 InvokeObjectPostAlterHook(RelationRelationId
,
15688 RelationGetRelid(rel
), 0);
15689 relation_close(rel
, NoLock
);
15693 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
15694 /* Fetch the list of indexes on toast relation if necessary */
15695 if (OidIsValid(reltoastrelid
))
15697 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
15699 reltoastidxids
= RelationGetIndexList(toastRel
);
15700 relation_close(toastRel
, lockmode
);
15704 * Relfilenumbers are not unique in databases across tablespaces, so we
15705 * need to allocate a new one in the new tablespace.
15707 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
15708 rel
->rd_rel
->relpersistence
);
15710 /* Open old and new relation */
15711 newrlocator
= rel
->rd_locator
;
15712 newrlocator
.relNumber
= newrelfilenumber
;
15713 newrlocator
.spcOid
= newTableSpace
;
15715 /* hand off to AM to actually create new rel storage and copy the data */
15716 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
15718 index_copy_data(rel
, newrlocator
);
15722 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
15723 table_relation_copy_data(rel
, &newrlocator
);
15727 * Update the pg_class row.
15729 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15730 * executed on pg_class or its indexes (the above copy wouldn't contain
15731 * the updated pg_class entry), but that's forbidden with
15732 * CheckRelationTableSpaceMove().
15734 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
15736 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15738 RelationAssumeNewRelfilelocator(rel
);
15740 relation_close(rel
, NoLock
);
15742 /* Make sure the reltablespace change is visible */
15743 CommandCounterIncrement();
15745 /* Move associated toast relation and/or indexes, too */
15746 if (OidIsValid(reltoastrelid
))
15747 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
15748 foreach(lc
, reltoastidxids
)
15749 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
15752 list_free(reltoastidxids
);
15756 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15757 * storage that have an interest in preserving tablespace.
15759 * Since these have no storage the tablespace can be updated with a simple
15760 * metadata only operation to update the tablespace.
15763 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
15766 * Shouldn't be called on relations having storage; these are processed in
15769 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15771 /* check if relation can be moved to its new tablespace */
15772 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15774 InvokeObjectPostAlterHook(RelationRelationId
,
15775 RelationGetRelid(rel
),
15780 /* Update can be done, so change reltablespace */
15781 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
15783 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15785 /* Make sure the reltablespace change is visible */
15786 CommandCounterIncrement();
15790 * Alter Table ALL ... SET TABLESPACE
15792 * Allows a user to move all objects of some type in a given tablespace in the
15793 * current database to another tablespace. Objects can be chosen based on the
15794 * owner of the object also, to allow users to move only their objects.
15795 * The user must have CREATE rights on the new tablespace, as usual. The main
15796 * permissions handling is done by the lower-level table move function.
15798 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15799 * lock can't be acquired then we ereport(ERROR).
15802 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
15804 List
*relations
= NIL
;
15806 ScanKeyData key
[1];
15808 TableScanDesc scan
;
15810 Oid orig_tablespaceoid
;
15811 Oid new_tablespaceoid
;
15812 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
15814 /* Ensure we were not asked to move something we can't */
15815 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
15816 stmt
->objtype
!= OBJECT_MATVIEW
)
15818 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15819 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15821 /* Get the orig and new tablespace OIDs */
15822 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
15823 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
15825 /* Can't move shared relations in to or out of pg_global */
15826 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15827 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
15828 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
15830 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15831 errmsg("cannot move relations in to or out of pg_global tablespace")));
15834 * Must have CREATE rights on the new tablespace, unless it is the
15835 * database default tablespace (which all users implicitly have CREATE
15838 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
15840 AclResult aclresult
;
15842 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
15844 if (aclresult
!= ACLCHECK_OK
)
15845 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
15846 get_tablespace_name(new_tablespaceoid
));
15850 * Now that the checks are done, check if we should set either to
15851 * InvalidOid because it is our database's default tablespace.
15853 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
15854 orig_tablespaceoid
= InvalidOid
;
15856 if (new_tablespaceoid
== MyDatabaseTableSpace
)
15857 new_tablespaceoid
= InvalidOid
;
15860 if (orig_tablespaceoid
== new_tablespaceoid
)
15861 return new_tablespaceoid
;
15864 * Walk the list of objects in the tablespace and move them. This will
15865 * only find objects in our database, of course.
15867 ScanKeyInit(&key
[0],
15868 Anum_pg_class_reltablespace
,
15869 BTEqualStrategyNumber
, F_OIDEQ
,
15870 ObjectIdGetDatum(orig_tablespaceoid
));
15872 rel
= table_open(RelationRelationId
, AccessShareLock
);
15873 scan
= table_beginscan_catalog(rel
, 1, key
);
15874 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
15876 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
15877 Oid relOid
= relForm
->oid
;
15880 * Do not move objects in pg_catalog as part of this, if an admin
15881 * really wishes to do so, they can issue the individual ALTER
15882 * commands directly.
15884 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15885 * (TOAST will be moved with the main table).
15887 if (IsCatalogNamespace(relForm
->relnamespace
) ||
15888 relForm
->relisshared
||
15889 isAnyTempNamespace(relForm
->relnamespace
) ||
15890 IsToastNamespace(relForm
->relnamespace
))
15893 /* Only move the object type requested */
15894 if ((stmt
->objtype
== OBJECT_TABLE
&&
15895 relForm
->relkind
!= RELKIND_RELATION
&&
15896 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
15897 (stmt
->objtype
== OBJECT_INDEX
&&
15898 relForm
->relkind
!= RELKIND_INDEX
&&
15899 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
15900 (stmt
->objtype
== OBJECT_MATVIEW
&&
15901 relForm
->relkind
!= RELKIND_MATVIEW
))
15904 /* Check if we are only moving objects owned by certain roles */
15905 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
15909 * Handle permissions-checking here since we are locking the tables
15910 * and also to avoid doing a bunch of work only to fail part-way. Note
15911 * that permissions will also be checked by AlterTableInternal().
15913 * Caller must be considered an owner on the table to move it.
15915 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
15916 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
15917 NameStr(relForm
->relname
));
15919 if (stmt
->nowait
&&
15920 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
15922 (errcode(ERRCODE_OBJECT_IN_USE
),
15923 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15924 get_namespace_name(relForm
->relnamespace
),
15925 NameStr(relForm
->relname
))));
15927 LockRelationOid(relOid
, AccessExclusiveLock
);
15929 /* Add to our list of objects to move */
15930 relations
= lappend_oid(relations
, relOid
);
15933 table_endscan(scan
);
15934 table_close(rel
, AccessShareLock
);
15936 if (relations
== NIL
)
15938 (errcode(ERRCODE_NO_DATA_FOUND
),
15939 errmsg("no matching relations in tablespace \"%s\" found",
15940 orig_tablespaceoid
== InvalidOid
? "(database default)" :
15941 get_tablespace_name(orig_tablespaceoid
))));
15943 /* Everything is locked, loop through and move all of the relations. */
15944 foreach(l
, relations
)
15947 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
15949 cmd
->subtype
= AT_SetTableSpace
;
15950 cmd
->name
= stmt
->new_tablespacename
;
15952 cmds
= lappend(cmds
, cmd
);
15954 EventTriggerAlterTableStart((Node
*) stmt
);
15955 /* OID is set by AlterTableInternal */
15956 AlterTableInternal(lfirst_oid(l
), cmds
, false);
15957 EventTriggerAlterTableEnd();
15960 return new_tablespaceoid
;
15964 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
15966 SMgrRelation dstrel
;
15969 * Since we copy the file directly without looking at the shared buffers,
15970 * we'd better first flush out any pages of the source relation that are
15971 * in shared buffers. We assume no new changes will be made while we are
15972 * holding exclusive lock on the rel.
15974 FlushRelationBuffers(rel
);
15977 * Create and copy all forks of the relation, and schedule unlinking of
15978 * old physical files.
15980 * NOTE: any conflict in relfilenumber value will be caught in
15981 * RelationCreateStorage().
15983 dstrel
= RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
15985 /* copy main fork */
15986 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
15987 rel
->rd_rel
->relpersistence
);
15989 /* copy those extra forks that exist */
15990 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
15991 forkNum
<= MAX_FORKNUM
; forkNum
++)
15993 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
15995 smgrcreate(dstrel
, forkNum
, false);
15998 * WAL log creation if the relation is persistent, or this is the
15999 * init fork of an unlogged relation.
16001 if (RelationIsPermanent(rel
) ||
16002 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
16003 forkNum
== INIT_FORKNUM
))
16004 log_smgrcreate(&newrlocator
, forkNum
);
16005 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
16006 rel
->rd_rel
->relpersistence
);
16010 /* drop old relation, and close new one */
16011 RelationDropStorage(rel
);
16016 * ALTER TABLE ENABLE/DISABLE TRIGGER
16018 * We just pass this off to trigger.c.
16021 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
16022 char fires_when
, bool skip_system
, bool recurse
,
16025 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
16026 fires_when
, skip_system
, recurse
,
16029 InvokeObjectPostAlterHook(RelationRelationId
,
16030 RelationGetRelid(rel
), 0);
16034 * ALTER TABLE ENABLE/DISABLE RULE
16036 * We just pass this off to rewriteDefine.c.
16039 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
16040 char fires_when
, LOCKMODE lockmode
)
16042 EnableDisableRule(rel
, rulename
, fires_when
);
16044 InvokeObjectPostAlterHook(RelationRelationId
,
16045 RelationGetRelid(rel
), 0);
16049 * ALTER TABLE INHERIT
16051 * Add a parent to the child's parents. This verifies that all the columns and
16052 * check constraints of the parent appear in the child and that they have the
16053 * same data types and expressions.
16056 ATPrepAddInherit(Relation child_rel
)
16058 if (child_rel
->rd_rel
->reloftype
)
16060 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16061 errmsg("cannot change inheritance of typed table")));
16063 if (child_rel
->rd_rel
->relispartition
)
16065 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16066 errmsg("cannot change inheritance of a partition")));
16068 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16070 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16071 errmsg("cannot change inheritance of partitioned table")));
16075 * Return the address of the new parent relation.
16077 static ObjectAddress
16078 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16080 Relation parent_rel
;
16082 ObjectAddress address
;
16083 const char *trigger_name
;
16086 * A self-exclusive lock is needed here. See the similar case in
16087 * MergeAttributes() for a full explanation.
16089 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
16092 * Must be owner of both parent and child -- child was checked by
16093 * ATSimplePermissions call in ATPrepCmd
16095 ATSimplePermissions(AT_AddInherit
, parent_rel
,
16096 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
16098 /* Permanent rels cannot inherit from temporary ones */
16099 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16100 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
16102 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16103 errmsg("cannot inherit from temporary relation \"%s\"",
16104 RelationGetRelationName(parent_rel
))));
16106 /* If parent rel is temp, it must belong to this session */
16107 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16108 !parent_rel
->rd_islocaltemp
)
16110 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16111 errmsg("cannot inherit from temporary relation of another session")));
16113 /* Ditto for the child */
16114 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16115 !child_rel
->rd_islocaltemp
)
16117 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16118 errmsg("cannot inherit to temporary relation of another session")));
16120 /* Prevent partitioned tables from becoming inheritance parents */
16121 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16123 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16124 errmsg("cannot inherit from partitioned table \"%s\"",
16125 parent
->relname
)));
16127 /* Likewise for partitions */
16128 if (parent_rel
->rd_rel
->relispartition
)
16130 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16131 errmsg("cannot inherit from a partition")));
16134 * Prevent circularity by seeing if proposed parent inherits from child.
16135 * (In particular, this disallows making a rel inherit from itself.)
16137 * This is not completely bulletproof because of race conditions: in
16138 * multi-level inheritance trees, someone else could concurrently be
16139 * making another inheritance link that closes the loop but does not join
16140 * either of the rels we have locked. Preventing that seems to require
16141 * exclusive locks on the entire inheritance tree, which is a cure worse
16142 * than the disease. find_all_inheritors() will cope with circularity
16143 * anyway, so don't sweat it too much.
16145 * We use weakest lock we can on child's children, namely AccessShareLock.
16147 children
= find_all_inheritors(RelationGetRelid(child_rel
),
16148 AccessShareLock
, NULL
);
16150 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
16152 (errcode(ERRCODE_DUPLICATE_TABLE
),
16153 errmsg("circular inheritance not allowed"),
16154 errdetail("\"%s\" is already a child of \"%s\".",
16156 RelationGetRelationName(child_rel
))));
16159 * If child_rel has row-level triggers with transition tables, we
16160 * currently don't allow it to become an inheritance child. See also
16161 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16163 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
16164 if (trigger_name
!= NULL
)
16166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16167 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16168 trigger_name
, RelationGetRelationName(child_rel
)),
16169 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16171 /* OK to create inheritance */
16172 CreateInheritance(child_rel
, parent_rel
, false);
16174 ObjectAddressSet(address
, RelationRelationId
,
16175 RelationGetRelid(parent_rel
));
16177 /* keep our lock on the parent relation until commit */
16178 table_close(parent_rel
, NoLock
);
16184 * CreateInheritance
16185 * Catalog manipulation portion of creating inheritance between a child
16186 * table and a parent table.
16188 * Common to ATExecAddInherit() and ATExecAttachPartition().
16191 CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16193 Relation catalogRelation
;
16196 HeapTuple inheritsTuple
;
16199 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16200 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16203 * Check for duplicates in the list of parents, and determine the highest
16204 * inhseqno already present; we'll use the next one for the new parent.
16205 * Also, if proposed child is a partition, it cannot already be
16208 * Note: we do not reject the case where the child already inherits from
16209 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16212 Anum_pg_inherits_inhrelid
,
16213 BTEqualStrategyNumber
, F_OIDEQ
,
16214 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16215 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
16216 true, NULL
, 1, &key
);
16218 /* inhseqno sequences start at 1 */
16220 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16222 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16224 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
16226 (errcode(ERRCODE_DUPLICATE_TABLE
),
16227 errmsg("relation \"%s\" would be inherited from more than once",
16228 RelationGetRelationName(parent_rel
))));
16230 if (inh
->inhseqno
> inhseqno
)
16231 inhseqno
= inh
->inhseqno
;
16233 systable_endscan(scan
);
16235 /* Match up the columns and bump attinhcount as needed */
16236 MergeAttributesIntoExisting(child_rel
, parent_rel
, ispartition
);
16238 /* Match up the constraints and bump coninhcount as needed */
16239 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
16242 * OK, it looks valid. Make the catalog entries that show inheritance.
16244 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
16245 RelationGetRelid(parent_rel
),
16248 parent_rel
->rd_rel
->relkind
==
16249 RELKIND_PARTITIONED_TABLE
);
16251 /* Now we're done with pg_inherits */
16252 table_close(catalogRelation
, RowExclusiveLock
);
16256 * Obtain the source-text form of the constraint expression for a check
16257 * constraint, given its pg_constraint tuple
16260 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
16262 Form_pg_constraint con
;
16267 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
16268 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
16270 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
16272 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
16273 ObjectIdGetDatum(con
->conrelid
));
16274 return TextDatumGetCString(expr
);
16278 * Determine whether two check constraints are functionally equivalent
16280 * The test we apply is to see whether they reverse-compile to the same
16281 * source string. This insulates us from issues like whether attributes
16282 * have the same physical column numbers in parent and child relations.
16284 * Note that we ignore enforceability as there are cases where constraints
16285 * with differing enforceability are allowed.
16288 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
16290 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
16291 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
16293 if (acon
->condeferrable
!= bcon
->condeferrable
||
16294 acon
->condeferred
!= bcon
->condeferred
||
16295 strcmp(decompile_conbin(a
, tupleDesc
),
16296 decompile_conbin(b
, tupleDesc
)) != 0)
16303 * Check columns in child table match up with columns in parent, and increment
16304 * their attinhcount.
16306 * Called by CreateInheritance
16308 * Currently all parent columns must be found in child. Missing columns are an
16309 * error. One day we might consider creating new columns like CREATE TABLE
16310 * does. However, that is widely unpopular --- in the common use case of
16311 * partitioned tables it's a foot-gun.
16313 * The data type must match exactly. If the parent column is NOT NULL then
16314 * the child must be as well. Defaults are not compared, however.
16317 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16320 TupleDesc parent_desc
;
16322 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
16323 parent_desc
= RelationGetDescr(parent_rel
);
16325 for (AttrNumber parent_attno
= 1; parent_attno
<= parent_desc
->natts
; parent_attno
++)
16327 Form_pg_attribute parent_att
= TupleDescAttr(parent_desc
, parent_attno
- 1);
16328 char *parent_attname
= NameStr(parent_att
->attname
);
16331 /* Ignore dropped columns in the parent. */
16332 if (parent_att
->attisdropped
)
16335 /* Find same column in child (matching on column name). */
16336 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
), parent_attname
);
16337 if (HeapTupleIsValid(tuple
))
16339 Form_pg_attribute child_att
= (Form_pg_attribute
) GETSTRUCT(tuple
);
16341 if (parent_att
->atttypid
!= child_att
->atttypid
||
16342 parent_att
->atttypmod
!= child_att
->atttypmod
)
16344 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16345 errmsg("child table \"%s\" has different type for column \"%s\"",
16346 RelationGetRelationName(child_rel
), parent_attname
)));
16348 if (parent_att
->attcollation
!= child_att
->attcollation
)
16350 (errcode(ERRCODE_COLLATION_MISMATCH
),
16351 errmsg("child table \"%s\" has different collation for column \"%s\"",
16352 RelationGetRelationName(child_rel
), parent_attname
)));
16355 * If the parent has a not-null constraint that's not NO INHERIT,
16356 * make sure the child has one too.
16358 * Other constraints are checked elsewhere.
16360 if (parent_att
->attnotnull
&& !child_att
->attnotnull
)
16364 contup
= findNotNullConstraintAttnum(RelationGetRelid(parent_rel
),
16365 parent_att
->attnum
);
16366 if (HeapTupleIsValid(contup
) &&
16367 !((Form_pg_constraint
) GETSTRUCT(contup
))->connoinherit
)
16369 errcode(ERRCODE_DATATYPE_MISMATCH
),
16370 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16371 parent_attname
, RelationGetRelationName(child_rel
)));
16375 * Child column must be generated if and only if parent column is.
16377 if (parent_att
->attgenerated
&& !child_att
->attgenerated
)
16379 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16380 errmsg("column \"%s\" in child table must be a generated column", parent_attname
)));
16381 if (child_att
->attgenerated
&& !parent_att
->attgenerated
)
16383 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16384 errmsg("column \"%s\" in child table must not be a generated column", parent_attname
)));
16387 * Regular inheritance children are independent enough not to
16388 * inherit identity columns. But partitions are integral part of
16389 * a partitioned table and inherit identity column.
16392 child_att
->attidentity
= parent_att
->attidentity
;
16395 * OK, bump the child column's inheritance count. (If we fail
16396 * later on, this change will just roll back.)
16398 if (pg_add_s16_overflow(child_att
->attinhcount
, 1,
16399 &child_att
->attinhcount
))
16401 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16402 errmsg("too many inheritance parents"));
16405 * In case of partitions, we must enforce that value of attislocal
16406 * is same in all partitions. (Note: there are only inherited
16407 * attributes in partitions)
16409 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16411 Assert(child_att
->attinhcount
== 1);
16412 child_att
->attislocal
= false;
16415 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
16416 heap_freetuple(tuple
);
16421 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16422 errmsg("child table is missing column \"%s\"", parent_attname
)));
16426 table_close(attrrel
, RowExclusiveLock
);
16430 * Check constraints in child table match up with constraints in parent,
16431 * and increment their coninhcount.
16433 * Constraints that are marked ONLY in the parent are ignored.
16435 * Called by CreateInheritance
16437 * Currently all constraints in parent must be present in the child. One day we
16438 * may consider adding new constraints like CREATE TABLE does.
16440 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16441 * constraints. As long as tables have more like 10 constraints it shouldn't be
16442 * a problem though. Even 100 constraints ought not be the end of the world.
16444 * XXX See MergeWithExistingConstraint too if you change this code.
16447 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
16449 Relation constraintrel
;
16450 SysScanDesc parent_scan
;
16451 ScanKeyData parent_key
;
16452 HeapTuple parent_tuple
;
16453 Oid parent_relid
= RelationGetRelid(parent_rel
);
16456 constraintrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16458 /* Outer loop scans through the parent's constraint definitions */
16459 ScanKeyInit(&parent_key
,
16460 Anum_pg_constraint_conrelid
,
16461 BTEqualStrategyNumber
, F_OIDEQ
,
16462 ObjectIdGetDatum(parent_relid
));
16463 parent_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16464 true, NULL
, 1, &parent_key
);
16466 attmap
= build_attrmap_by_name(RelationGetDescr(parent_rel
),
16467 RelationGetDescr(child_rel
),
16470 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
16472 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
16473 SysScanDesc child_scan
;
16474 ScanKeyData child_key
;
16475 HeapTuple child_tuple
;
16476 AttrNumber parent_attno
;
16477 bool found
= false;
16479 if (parent_con
->contype
!= CONSTRAINT_CHECK
&&
16480 parent_con
->contype
!= CONSTRAINT_NOTNULL
)
16483 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16484 if (parent_con
->connoinherit
)
16487 if (parent_con
->contype
== CONSTRAINT_NOTNULL
)
16488 parent_attno
= extractNotNullColumn(parent_tuple
);
16490 parent_attno
= InvalidAttrNumber
;
16492 /* Search for a child constraint matching this one */
16493 ScanKeyInit(&child_key
,
16494 Anum_pg_constraint_conrelid
,
16495 BTEqualStrategyNumber
, F_OIDEQ
,
16496 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16497 child_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16498 true, NULL
, 1, &child_key
);
16500 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
16502 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
16503 HeapTuple child_copy
;
16505 if (child_con
->contype
!= parent_con
->contype
)
16509 * CHECK constraint are matched by constraint name, NOT NULL ones
16510 * by attribute number.
16512 if (child_con
->contype
== CONSTRAINT_CHECK
)
16514 if (strcmp(NameStr(parent_con
->conname
),
16515 NameStr(child_con
->conname
)) != 0)
16518 else if (child_con
->contype
== CONSTRAINT_NOTNULL
)
16520 Form_pg_attribute parent_attr
;
16521 Form_pg_attribute child_attr
;
16522 AttrNumber child_attno
;
16524 parent_attr
= TupleDescAttr(parent_rel
->rd_att
, parent_attno
- 1);
16525 child_attno
= extractNotNullColumn(child_tuple
);
16526 if (parent_attno
!= attmap
->attnums
[child_attno
- 1])
16529 child_attr
= TupleDescAttr(child_rel
->rd_att
, child_attno
- 1);
16530 /* there shouldn't be constraints on dropped columns */
16531 if (parent_attr
->attisdropped
|| child_attr
->attisdropped
)
16532 elog(ERROR
, "found not-null constraint on dropped columns");
16535 if (child_con
->contype
== CONSTRAINT_CHECK
&&
16536 !constraints_equivalent(parent_tuple
, child_tuple
, RelationGetDescr(constraintrel
)))
16538 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16539 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16540 RelationGetRelationName(child_rel
), NameStr(parent_con
->conname
))));
16543 * If the child constraint is "no inherit" then cannot merge
16545 if (child_con
->connoinherit
)
16547 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16548 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16549 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16552 * If the child constraint is "not valid" then cannot merge with a
16553 * valid parent constraint
16555 if (parent_con
->convalidated
&& child_con
->conenforced
&&
16556 !child_con
->convalidated
)
16558 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16559 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16560 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16563 * A non-enforced child constraint cannot be merged with an
16564 * enforced parent constraint. However, the reverse is allowed,
16565 * where the child constraint is enforced.
16567 if (parent_con
->conenforced
&& !child_con
->conenforced
)
16569 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16570 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
16571 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16574 * OK, bump the child constraint's inheritance count. (If we fail
16575 * later on, this change will just roll back.)
16577 child_copy
= heap_copytuple(child_tuple
);
16578 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
16580 if (pg_add_s16_overflow(child_con
->coninhcount
, 1,
16581 &child_con
->coninhcount
))
16583 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16584 errmsg("too many inheritance parents"));
16587 * In case of partitions, an inherited constraint must be
16588 * inherited only once since it cannot have multiple parents and
16589 * it is never considered local.
16591 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16593 Assert(child_con
->coninhcount
== 1);
16594 child_con
->conislocal
= false;
16597 CatalogTupleUpdate(constraintrel
, &child_copy
->t_self
, child_copy
);
16598 heap_freetuple(child_copy
);
16604 systable_endscan(child_scan
);
16608 if (parent_con
->contype
== CONSTRAINT_NOTNULL
)
16610 errcode(ERRCODE_DATATYPE_MISMATCH
),
16611 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16612 get_attname(parent_relid
,
16613 extractNotNullColumn(parent_tuple
),
16615 RelationGetRelationName(child_rel
)));
16618 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16619 errmsg("child table is missing constraint \"%s\"",
16620 NameStr(parent_con
->conname
))));
16624 systable_endscan(parent_scan
);
16625 table_close(constraintrel
, RowExclusiveLock
);
16629 * ALTER TABLE NO INHERIT
16631 * Return value is the address of the relation that is no longer parent.
16633 static ObjectAddress
16634 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16636 ObjectAddress address
;
16637 Relation parent_rel
;
16639 if (rel
->rd_rel
->relispartition
)
16641 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16642 errmsg("cannot change inheritance of a partition")));
16645 * AccessShareLock on the parent is probably enough, seeing that DROP
16646 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16647 * be inspecting the parent's schema.
16649 parent_rel
= table_openrv(parent
, AccessShareLock
);
16652 * We don't bother to check ownership of the parent table --- ownership of
16653 * the child is presumed enough rights.
16656 /* Off to RemoveInheritance() where most of the work happens */
16657 RemoveInheritance(rel
, parent_rel
, false);
16659 ObjectAddressSet(address
, RelationRelationId
,
16660 RelationGetRelid(parent_rel
));
16662 /* keep our lock on the parent relation until commit */
16663 table_close(parent_rel
, NoLock
);
16669 * MarkInheritDetached
16671 * Set inhdetachpending for a partition, for ATExecDetachPartition
16672 * in concurrent mode. While at it, verify that no other partition is
16673 * already pending detach.
16676 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
16678 Relation catalogRelation
;
16681 HeapTuple inheritsTuple
;
16682 bool found
= false;
16684 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16687 * Find pg_inherits entries by inhparent. (We need to scan them all in
16688 * order to verify that no other partition is pending detach.)
16690 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16692 Anum_pg_inherits_inhparent
,
16693 BTEqualStrategyNumber
, F_OIDEQ
,
16694 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16695 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
16696 true, NULL
, 1, &key
);
16698 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16700 Form_pg_inherits inhForm
;
16702 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16703 if (inhForm
->inhdetachpending
)
16705 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16706 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16707 get_rel_name(inhForm
->inhrelid
),
16708 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
16709 RelationGetRelationName(parent_rel
)),
16710 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16712 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
16716 newtup
= heap_copytuple(inheritsTuple
);
16717 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
16719 CatalogTupleUpdate(catalogRelation
,
16720 &inheritsTuple
->t_self
,
16723 heap_freetuple(newtup
);
16724 /* keep looking, to ensure we catch others pending detach */
16729 systable_endscan(scan
);
16730 table_close(catalogRelation
, RowExclusiveLock
);
16734 (errcode(ERRCODE_UNDEFINED_TABLE
),
16735 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16736 RelationGetRelationName(child_rel
),
16737 RelationGetRelationName(parent_rel
))));
16741 * RemoveInheritance
16743 * Drop a parent from the child's parents. This just adjusts the attinhcount
16744 * and attislocal of the columns and removes the pg_inherit and pg_depend
16745 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16747 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16748 * up attislocal stays true, which means if a child is ever removed from a
16749 * parent then its columns will never be automatically dropped which may
16750 * surprise. But at least we'll never surprise by dropping columns someone
16751 * isn't expecting to be dropped which would actually mean data loss.
16753 * coninhcount and conislocal for inherited constraints are adjusted in
16754 * exactly the same way.
16756 * Common to ATExecDropInherit() and ATExecDetachPartition().
16759 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
16761 Relation catalogRelation
;
16763 ScanKeyData key
[3];
16764 HeapTuple attributeTuple
,
16770 bool is_partitioning
;
16772 is_partitioning
= (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16774 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
16775 RelationGetRelid(parent_rel
),
16777 RelationGetRelationName(child_rel
));
16780 if (is_partitioning
)
16782 (errcode(ERRCODE_UNDEFINED_TABLE
),
16783 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16784 RelationGetRelationName(child_rel
),
16785 RelationGetRelationName(parent_rel
))));
16788 (errcode(ERRCODE_UNDEFINED_TABLE
),
16789 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16790 RelationGetRelationName(parent_rel
),
16791 RelationGetRelationName(child_rel
))));
16795 * Search through child columns looking for ones matching parent rel
16797 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
16798 ScanKeyInit(&key
[0],
16799 Anum_pg_attribute_attrelid
,
16800 BTEqualStrategyNumber
, F_OIDEQ
,
16801 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16802 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
16803 true, NULL
, 1, key
);
16804 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
16806 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
16808 /* Ignore if dropped or not inherited */
16809 if (att
->attisdropped
)
16811 if (att
->attinhcount
<= 0)
16814 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
16815 NameStr(att
->attname
)))
16817 /* Decrement inhcount and possibly set islocal to true */
16818 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
16819 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
16821 copy_att
->attinhcount
--;
16822 if (copy_att
->attinhcount
== 0)
16823 copy_att
->attislocal
= true;
16825 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16826 heap_freetuple(copyTuple
);
16829 systable_endscan(scan
);
16830 table_close(catalogRelation
, RowExclusiveLock
);
16833 * Likewise, find inherited check and not-null constraints and disinherit
16834 * them. To do this, we first need a list of the names of the parent's
16835 * check constraints. (We cheat a bit by only checking for name matches,
16836 * assuming that the expressions will match.)
16838 * For NOT NULL columns, we store column numbers to match, mapping them in
16839 * to the child rel's attribute numbers.
16841 attmap
= build_attrmap_by_name(RelationGetDescr(child_rel
),
16842 RelationGetDescr(parent_rel
),
16845 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16846 ScanKeyInit(&key
[0],
16847 Anum_pg_constraint_conrelid
,
16848 BTEqualStrategyNumber
, F_OIDEQ
,
16849 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16850 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16851 true, NULL
, 1, key
);
16856 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16858 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16860 if (con
->connoinherit
)
16863 if (con
->contype
== CONSTRAINT_CHECK
)
16864 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
16865 if (con
->contype
== CONSTRAINT_NOTNULL
)
16867 AttrNumber parent_attno
= extractNotNullColumn(constraintTuple
);
16869 nncolumns
= lappend_int(nncolumns
, attmap
->attnums
[parent_attno
- 1]);
16873 systable_endscan(scan
);
16875 /* Now scan the child's constraints to find matches */
16876 ScanKeyInit(&key
[0],
16877 Anum_pg_constraint_conrelid
,
16878 BTEqualStrategyNumber
, F_OIDEQ
,
16879 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16880 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16881 true, NULL
, 1, key
);
16883 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16885 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16886 bool match
= false;
16889 * Match CHECK constraints by name, not-null constraints by column
16890 * number, and ignore all others.
16892 if (con
->contype
== CONSTRAINT_CHECK
)
16894 foreach_ptr(char, chkname
, connames
)
16896 if (con
->contype
== CONSTRAINT_CHECK
&&
16897 strcmp(NameStr(con
->conname
), chkname
) == 0)
16900 connames
= foreach_delete_current(connames
, chkname
);
16905 else if (con
->contype
== CONSTRAINT_NOTNULL
)
16907 AttrNumber child_attno
= extractNotNullColumn(constraintTuple
);
16909 foreach_int(prevattno
, nncolumns
)
16911 if (prevattno
== child_attno
)
16914 nncolumns
= foreach_delete_current(nncolumns
, prevattno
);
16924 /* Decrement inhcount and possibly set islocal to true */
16925 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
16926 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
16928 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
16929 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
16930 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
16932 copy_con
->coninhcount
--;
16933 if (copy_con
->coninhcount
== 0)
16934 copy_con
->conislocal
= true;
16936 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16937 heap_freetuple(copyTuple
);
16941 /* We should have matched all constraints */
16942 if (connames
!= NIL
|| nncolumns
!= NIL
)
16943 elog(ERROR
, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
16944 list_length(connames
) + list_length(nncolumns
),
16945 RelationGetRelationName(child_rel
), RelationGetRelationName(parent_rel
));
16947 systable_endscan(scan
);
16948 table_close(catalogRelation
, RowExclusiveLock
);
16950 drop_parent_dependency(RelationGetRelid(child_rel
),
16951 RelationRelationId
,
16952 RelationGetRelid(parent_rel
),
16953 child_dependency_type(is_partitioning
));
16956 * Post alter hook of this inherits. Since object_access_hook doesn't take
16957 * multiple object identifiers, we relay oid of parent relation using
16958 * auxiliary_id argument.
16960 InvokeObjectPostAlterHookArg(InheritsRelationId
,
16961 RelationGetRelid(child_rel
), 0,
16962 RelationGetRelid(parent_rel
), false);
16966 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16967 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16968 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16969 * be TypeRelationId). There's no convenient way to do this, so go trawling
16970 * through pg_depend.
16973 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
16974 DependencyType deptype
)
16976 Relation catalogRelation
;
16978 ScanKeyData key
[3];
16979 HeapTuple depTuple
;
16981 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
16983 ScanKeyInit(&key
[0],
16984 Anum_pg_depend_classid
,
16985 BTEqualStrategyNumber
, F_OIDEQ
,
16986 ObjectIdGetDatum(RelationRelationId
));
16987 ScanKeyInit(&key
[1],
16988 Anum_pg_depend_objid
,
16989 BTEqualStrategyNumber
, F_OIDEQ
,
16990 ObjectIdGetDatum(relid
));
16991 ScanKeyInit(&key
[2],
16992 Anum_pg_depend_objsubid
,
16993 BTEqualStrategyNumber
, F_INT4EQ
,
16996 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
16999 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
17001 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
17003 if (dep
->refclassid
== refclassid
&&
17004 dep
->refobjid
== refobjid
&&
17005 dep
->refobjsubid
== 0 &&
17006 dep
->deptype
== deptype
)
17007 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
17010 systable_endscan(scan
);
17011 table_close(catalogRelation
, RowExclusiveLock
);
17017 * Attach a table to a composite type, as though it had been created with CREATE
17018 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17019 * subject table must not have inheritance parents. These restrictions ensure
17020 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17022 * The address of the type is returned.
17024 static ObjectAddress
17025 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
17027 Oid relid
= RelationGetRelid(rel
);
17029 Form_pg_type typeform
;
17031 Relation inheritsRelation
,
17035 AttrNumber table_attno
,
17037 TupleDesc typeTupleDesc
,
17039 ObjectAddress tableobj
,
17041 HeapTuple classtuple
;
17043 /* Validate the type. */
17044 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
17045 check_of_type(typetuple
);
17046 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
17047 typeid = typeform
->oid
;
17049 /* Fail if the table has any inheritance parents. */
17050 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
17052 Anum_pg_inherits_inhrelid
,
17053 BTEqualStrategyNumber
, F_OIDEQ
,
17054 ObjectIdGetDatum(relid
));
17055 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
17056 true, NULL
, 1, &key
);
17057 if (HeapTupleIsValid(systable_getnext(scan
)))
17059 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17060 errmsg("typed tables cannot inherit")));
17061 systable_endscan(scan
);
17062 table_close(inheritsRelation
, AccessShareLock
);
17065 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17066 * require that the order also match. However, attnotnull need not match.
17068 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
17069 tableTupleDesc
= RelationGetDescr(rel
);
17071 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
17073 Form_pg_attribute type_attr
,
17075 const char *type_attname
,
17078 /* Get the next non-dropped type attribute. */
17079 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
17080 if (type_attr
->attisdropped
)
17082 type_attname
= NameStr(type_attr
->attname
);
17084 /* Get the next non-dropped table attribute. */
17087 if (table_attno
> tableTupleDesc
->natts
)
17089 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17090 errmsg("table is missing column \"%s\"",
17092 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
17094 } while (table_attr
->attisdropped
);
17095 table_attname
= NameStr(table_attr
->attname
);
17097 /* Compare name. */
17098 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
17100 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17101 errmsg("table has column \"%s\" where type requires \"%s\"",
17102 table_attname
, type_attname
)));
17104 /* Compare type. */
17105 if (table_attr
->atttypid
!= type_attr
->atttypid
||
17106 table_attr
->atttypmod
!= type_attr
->atttypmod
||
17107 table_attr
->attcollation
!= type_attr
->attcollation
)
17109 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17110 errmsg("table \"%s\" has different type for column \"%s\"",
17111 RelationGetRelationName(rel
), type_attname
)));
17113 ReleaseTupleDesc(typeTupleDesc
);
17115 /* Any remaining columns at the end of the table had better be dropped. */
17116 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
17118 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
17121 if (!table_attr
->attisdropped
)
17123 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17124 errmsg("table has extra column \"%s\"",
17125 NameStr(table_attr
->attname
))));
17128 /* If the table was already typed, drop the existing dependency. */
17129 if (rel
->rd_rel
->reloftype
)
17130 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17131 DEPENDENCY_NORMAL
);
17133 /* Record a dependency on the new type. */
17134 tableobj
.classId
= RelationRelationId
;
17135 tableobj
.objectId
= relid
;
17136 tableobj
.objectSubId
= 0;
17137 typeobj
.classId
= TypeRelationId
;
17138 typeobj
.objectId
= typeid;
17139 typeobj
.objectSubId
= 0;
17140 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
17142 /* Update pg_class.reloftype */
17143 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17144 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17145 if (!HeapTupleIsValid(classtuple
))
17146 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17147 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
17148 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
17150 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17152 heap_freetuple(classtuple
);
17153 table_close(relationRelation
, RowExclusiveLock
);
17155 ReleaseSysCache(typetuple
);
17161 * ALTER TABLE NOT OF
17163 * Detach a typed table from its originating type. Just clear reloftype and
17164 * remove the dependency.
17167 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
17169 Oid relid
= RelationGetRelid(rel
);
17170 Relation relationRelation
;
17173 if (!OidIsValid(rel
->rd_rel
->reloftype
))
17175 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17176 errmsg("\"%s\" is not a typed table",
17177 RelationGetRelationName(rel
))));
17180 * We don't bother to check ownership of the type --- ownership of the
17181 * table is presumed enough rights. No lock required on the type, either.
17184 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17185 DEPENDENCY_NORMAL
);
17187 /* Clear pg_class.reloftype */
17188 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17189 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17190 if (!HeapTupleIsValid(tuple
))
17191 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17192 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
17193 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
17195 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17197 heap_freetuple(tuple
);
17198 table_close(relationRelation
, RowExclusiveLock
);
17202 * relation_mark_replica_identity: Update a table's replica identity
17204 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17205 * index. Otherwise, it must be InvalidOid.
17207 * Caller had better hold an exclusive lock on the relation, as the results
17208 * of running two of these concurrently wouldn't be pretty.
17211 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
17216 HeapTuple pg_class_tuple
;
17217 HeapTuple pg_index_tuple
;
17218 Form_pg_class pg_class_form
;
17219 Form_pg_index pg_index_form
;
17223 * Check whether relreplident has changed, and update it if so.
17225 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17226 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
17227 ObjectIdGetDatum(RelationGetRelid(rel
)));
17228 if (!HeapTupleIsValid(pg_class_tuple
))
17229 elog(ERROR
, "cache lookup failed for relation \"%s\"",
17230 RelationGetRelationName(rel
));
17231 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
17232 if (pg_class_form
->relreplident
!= ri_type
)
17234 pg_class_form
->relreplident
= ri_type
;
17235 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
17237 table_close(pg_class
, RowExclusiveLock
);
17238 heap_freetuple(pg_class_tuple
);
17241 * Update the per-index indisreplident flags correctly.
17243 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
17244 foreach(index
, RelationGetIndexList(rel
))
17246 Oid thisIndexOid
= lfirst_oid(index
);
17247 bool dirty
= false;
17249 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
17250 ObjectIdGetDatum(thisIndexOid
));
17251 if (!HeapTupleIsValid(pg_index_tuple
))
17252 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
17253 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
17255 if (thisIndexOid
== indexOid
)
17257 /* Set the bit if not already set. */
17258 if (!pg_index_form
->indisreplident
)
17261 pg_index_form
->indisreplident
= true;
17266 /* Unset the bit if set. */
17267 if (pg_index_form
->indisreplident
)
17270 pg_index_form
->indisreplident
= false;
17276 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
17277 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
17278 InvalidOid
, is_internal
);
17281 * Invalidate the relcache for the table, so that after we commit
17282 * all sessions will refresh the table's replica identity index
17283 * before attempting any UPDATE or DELETE on the table. (If we
17284 * changed the table's pg_class row above, then a relcache inval
17285 * is already queued due to that; but we might not have.)
17287 CacheInvalidateRelcache(rel
);
17289 heap_freetuple(pg_index_tuple
);
17292 table_close(pg_index
, RowExclusiveLock
);
17296 * ALTER TABLE <name> REPLICA IDENTITY ...
17299 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
17305 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
17307 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17310 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
17312 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17315 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
17317 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17320 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
17322 /* fallthrough */ ;
17325 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
17327 /* Check that the index exists */
17328 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
17329 if (!OidIsValid(indexOid
))
17331 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17332 errmsg("index \"%s\" for table \"%s\" does not exist",
17333 stmt
->name
, RelationGetRelationName(rel
))));
17335 indexRel
= index_open(indexOid
, ShareLock
);
17337 /* Check that the index is on the relation we're altering. */
17338 if (indexRel
->rd_index
== NULL
||
17339 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
17341 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17342 errmsg("\"%s\" is not an index for table \"%s\"",
17343 RelationGetRelationName(indexRel
),
17344 RelationGetRelationName(rel
))));
17347 * The AM must support uniqueness, and the index must in fact be unique.
17348 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17349 * exclusion), we can use that too.
17351 if ((!indexRel
->rd_indam
->amcanunique
||
17352 !indexRel
->rd_index
->indisunique
) &&
17353 !(indexRel
->rd_index
->indisunique
&& indexRel
->rd_index
->indisexclusion
))
17355 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17356 errmsg("cannot use non-unique index \"%s\" as replica identity",
17357 RelationGetRelationName(indexRel
))));
17358 /* Deferred indexes are not guaranteed to be always unique. */
17359 if (!indexRel
->rd_index
->indimmediate
)
17361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17362 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17363 RelationGetRelationName(indexRel
))));
17364 /* Expression indexes aren't supported. */
17365 if (RelationGetIndexExpressions(indexRel
) != NIL
)
17367 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17368 errmsg("cannot use expression index \"%s\" as replica identity",
17369 RelationGetRelationName(indexRel
))));
17370 /* Predicate indexes aren't supported. */
17371 if (RelationGetIndexPredicate(indexRel
) != NIL
)
17373 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17374 errmsg("cannot use partial index \"%s\" as replica identity",
17375 RelationGetRelationName(indexRel
))));
17377 /* Check index for nullable columns. */
17378 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
17380 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
17381 Form_pg_attribute attr
;
17384 * Reject any other system columns. (Going forward, we'll disallow
17385 * indexes containing such columns in the first place, but they might
17386 * exist in older branches.)
17390 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
17391 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17392 RelationGetRelationName(indexRel
), attno
)));
17394 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
17395 if (!attr
->attnotnull
)
17397 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17398 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17399 RelationGetRelationName(indexRel
),
17400 NameStr(attr
->attname
))));
17403 /* This index is suitable for use as a replica identity. Mark it. */
17404 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
17406 index_close(indexRel
, NoLock
);
17410 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17413 ATExecSetRowSecurity(Relation rel
, bool rls
)
17419 relid
= RelationGetRelid(rel
);
17421 /* Pull the record for this relation and update it */
17422 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17424 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17426 if (!HeapTupleIsValid(tuple
))
17427 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17429 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
17430 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17432 InvokeObjectPostAlterHook(RelationRelationId
,
17433 RelationGetRelid(rel
), 0);
17435 table_close(pg_class
, RowExclusiveLock
);
17436 heap_freetuple(tuple
);
17440 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17443 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
17449 relid
= RelationGetRelid(rel
);
17451 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17453 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17455 if (!HeapTupleIsValid(tuple
))
17456 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17458 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
17459 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17461 InvokeObjectPostAlterHook(RelationRelationId
,
17462 RelationGetRelid(rel
), 0);
17464 table_close(pg_class
, RowExclusiveLock
);
17465 heap_freetuple(tuple
);
17469 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17472 ATExecGenericOptions(Relation rel
, List
*options
)
17475 ForeignServer
*server
;
17476 ForeignDataWrapper
*fdw
;
17479 Datum repl_val
[Natts_pg_foreign_table
];
17480 bool repl_null
[Natts_pg_foreign_table
];
17481 bool repl_repl
[Natts_pg_foreign_table
];
17483 Form_pg_foreign_table tableform
;
17485 if (options
== NIL
)
17488 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
17490 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
17491 ObjectIdGetDatum(rel
->rd_id
));
17492 if (!HeapTupleIsValid(tuple
))
17494 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17495 errmsg("foreign table \"%s\" does not exist",
17496 RelationGetRelationName(rel
))));
17497 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
17498 server
= GetForeignServer(tableform
->ftserver
);
17499 fdw
= GetForeignDataWrapper(server
->fdwid
);
17501 memset(repl_val
, 0, sizeof(repl_val
));
17502 memset(repl_null
, false, sizeof(repl_null
));
17503 memset(repl_repl
, false, sizeof(repl_repl
));
17505 /* Extract the current options */
17506 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
17508 Anum_pg_foreign_table_ftoptions
,
17511 datum
= PointerGetDatum(NULL
);
17513 /* Transform the options */
17514 datum
= transformGenericOptions(ForeignTableRelationId
,
17517 fdw
->fdwvalidator
);
17519 if (PointerIsValid(DatumGetPointer(datum
)))
17520 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
17522 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17524 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17526 /* Everything looks good - update the tuple */
17528 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
17529 repl_val
, repl_null
, repl_repl
);
17531 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
17534 * Invalidate relcache so that all sessions will refresh any cached plans
17535 * that might depend on the old options.
17537 CacheInvalidateRelcache(rel
);
17539 InvokeObjectPostAlterHook(ForeignTableRelationId
,
17540 RelationGetRelid(rel
), 0);
17542 table_close(ftrel
, RowExclusiveLock
);
17544 heap_freetuple(tuple
);
17548 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17550 * Return value is the address of the modified column
17552 static ObjectAddress
17553 ATExecSetCompression(Relation rel
,
17554 const char *column
,
17560 Form_pg_attribute atttableform
;
17564 ObjectAddress address
;
17566 compression
= strVal(newValue
);
17568 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
17570 /* copy the cache entry so we can scribble on it below */
17571 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
17572 if (!HeapTupleIsValid(tuple
))
17574 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17575 errmsg("column \"%s\" of relation \"%s\" does not exist",
17576 column
, RelationGetRelationName(rel
))));
17578 /* prevent them from altering a system attribute */
17579 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
17580 attnum
= atttableform
->attnum
;
17583 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17584 errmsg("cannot alter system column \"%s\"", column
)));
17587 * Check that column type is compressible, then get the attribute
17588 * compression method code
17590 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
17592 /* update pg_attribute entry */
17593 atttableform
->attcompression
= cmethod
;
17594 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
17596 InvokeObjectPostAlterHook(RelationRelationId
,
17597 RelationGetRelid(rel
),
17601 * Apply the change to indexes as well (only for simple index columns,
17602 * matching behavior of index.c ConstructTupleDescriptor()).
17604 SetIndexStorageProperties(rel
, attrel
, attnum
,
17609 heap_freetuple(tuple
);
17611 table_close(attrel
, RowExclusiveLock
);
17613 /* make changes visible */
17614 CommandCounterIncrement();
17616 ObjectAddressSubSet(address
, RelationRelationId
,
17617 RelationGetRelid(rel
), attnum
);
17623 * Preparation phase for SET LOGGED/UNLOGGED
17625 * This verifies that we're not trying to change a temp table. Also,
17626 * existing foreign key constraints are checked to avoid ending up with
17627 * permanent tables referencing unlogged tables.
17630 ATPrepChangePersistence(AlteredTableInfo
*tab
, Relation rel
, bool toLogged
)
17632 Relation pg_constraint
;
17635 ScanKeyData skey
[1];
17638 * Disallow changing status for a temp table. Also verify whether we can
17639 * get away with doing nothing; in such cases we don't need to run the
17640 * checks below, either.
17642 switch (rel
->rd_rel
->relpersistence
)
17644 case RELPERSISTENCE_TEMP
:
17646 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17647 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17648 RelationGetRelationName(rel
)),
17651 case RELPERSISTENCE_PERMANENT
:
17653 /* nothing to do */
17656 case RELPERSISTENCE_UNLOGGED
:
17658 /* nothing to do */
17664 * Check that the table is not part of any publication when changing to
17665 * UNLOGGED, as UNLOGGED tables can't be published.
17668 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
17670 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
17671 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17672 RelationGetRelationName(rel
)),
17673 errdetail("Unlogged relations cannot be replicated.")));
17676 * Check existing foreign key constraints to preserve the invariant that
17677 * permanent tables cannot reference unlogged ones. Self-referencing
17678 * foreign keys can safely be ignored.
17680 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
17683 * Scan conrelid if changing to permanent, else confrelid. This also
17684 * determines whether a useful index exists.
17686 ScanKeyInit(&skey
[0],
17687 toLogged
? Anum_pg_constraint_conrelid
:
17688 Anum_pg_constraint_confrelid
,
17689 BTEqualStrategyNumber
, F_OIDEQ
,
17690 ObjectIdGetDatum(RelationGetRelid(rel
)));
17691 scan
= systable_beginscan(pg_constraint
,
17692 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
17693 true, NULL
, 1, skey
);
17695 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
17697 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
17699 if (con
->contype
== CONSTRAINT_FOREIGN
)
17702 Relation foreignrel
;
17704 /* the opposite end of what we used as scankey */
17705 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
17707 /* ignore if self-referencing */
17708 if (RelationGetRelid(rel
) == foreignrelid
)
17711 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
17715 if (!RelationIsPermanent(foreignrel
))
17717 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17718 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17719 RelationGetRelationName(rel
),
17720 RelationGetRelationName(foreignrel
)),
17721 errtableconstraint(rel
, NameStr(con
->conname
))));
17725 if (RelationIsPermanent(foreignrel
))
17727 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17728 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17729 RelationGetRelationName(rel
),
17730 RelationGetRelationName(foreignrel
)),
17731 errtableconstraint(rel
, NameStr(con
->conname
))));
17734 relation_close(foreignrel
, AccessShareLock
);
17738 systable_endscan(scan
);
17740 table_close(pg_constraint
, AccessShareLock
);
17742 /* force rewrite if necessary; see comment in ATRewriteTables */
17743 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
17745 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
17747 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
17748 tab
->chgPersistence
= true;
17752 * Execute ALTER TABLE SET SCHEMA
17755 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
17762 ObjectAddresses
*objsMoved
;
17763 ObjectAddress myself
;
17765 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
17766 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
17767 RangeVarCallbackForAlterRelation
,
17770 if (!OidIsValid(relid
))
17773 (errmsg("relation \"%s\" does not exist, skipping",
17774 stmt
->relation
->relname
)));
17775 return InvalidObjectAddress
;
17778 rel
= relation_open(relid
, NoLock
);
17780 oldNspOid
= RelationGetNamespace(rel
);
17782 /* If it's an owned sequence, disallow moving it by itself. */
17783 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
17788 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
17789 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
17791 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17792 errmsg("cannot move an owned sequence into another schema"),
17793 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17794 RelationGetRelationName(rel
),
17795 get_rel_name(tableId
))));
17798 /* Get and lock schema OID and check its permissions. */
17799 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
17800 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
17802 /* common checks on switching namespaces */
17803 CheckSetNamespace(oldNspOid
, nspOid
);
17805 objsMoved
= new_object_addresses();
17806 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
17807 free_object_addresses(objsMoved
);
17809 ObjectAddressSet(myself
, RelationRelationId
, relid
);
17812 *oldschema
= oldNspOid
;
17814 /* close rel, but keep lock until commit */
17815 relation_close(rel
, NoLock
);
17821 * The guts of relocating a table or materialized view to another namespace:
17822 * besides moving the relation itself, its dependent objects are relocated to
17826 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
17827 ObjectAddresses
*objsMoved
)
17831 Assert(objsMoved
!= NULL
);
17833 /* OK, modify the pg_class row and pg_depend entry */
17834 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
17836 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
17837 nspOid
, true, objsMoved
);
17839 /* Fix the table's row type too, if it has one */
17840 if (OidIsValid(rel
->rd_rel
->reltype
))
17841 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
, nspOid
,
17842 false, /* isImplicitArray */
17843 false, /* ignoreDependent */
17844 false, /* errorOnTableType */
17847 /* Fix other dependent stuff */
17848 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
17849 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
17850 objsMoved
, AccessExclusiveLock
);
17851 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
17854 table_close(classRel
, RowExclusiveLock
);
17858 * The guts of relocating a relation to another namespace: fix the pg_class
17859 * entry, and the pg_depend entry if any. Caller must already have
17860 * opened and write-locked pg_class.
17863 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
17864 Oid oldNspOid
, Oid newNspOid
,
17865 bool hasDependEntry
,
17866 ObjectAddresses
*objsMoved
)
17868 HeapTuple classTup
;
17869 Form_pg_class classForm
;
17870 ObjectAddress thisobj
;
17871 bool already_done
= false;
17873 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
17874 classTup
= SearchSysCacheLockedCopy1(RELOID
, ObjectIdGetDatum(relOid
));
17875 if (!HeapTupleIsValid(classTup
))
17876 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
17877 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
17879 Assert(classForm
->relnamespace
== oldNspOid
);
17881 thisobj
.classId
= RelationRelationId
;
17882 thisobj
.objectId
= relOid
;
17883 thisobj
.objectSubId
= 0;
17886 * If the object has already been moved, don't move it again. If it's
17887 * already in the right place, don't move it, but still fire the object
17890 already_done
= object_address_present(&thisobj
, objsMoved
);
17891 if (!already_done
&& oldNspOid
!= newNspOid
)
17893 ItemPointerData otid
= classTup
->t_self
;
17895 /* check for duplicate name (more friendly than unique-index failure) */
17896 if (get_relname_relid(NameStr(classForm
->relname
),
17897 newNspOid
) != InvalidOid
)
17899 (errcode(ERRCODE_DUPLICATE_TABLE
),
17900 errmsg("relation \"%s\" already exists in schema \"%s\"",
17901 NameStr(classForm
->relname
),
17902 get_namespace_name(newNspOid
))));
17904 /* classTup is a copy, so OK to scribble on */
17905 classForm
->relnamespace
= newNspOid
;
17907 CatalogTupleUpdate(classRel
, &otid
, classTup
);
17908 UnlockTuple(classRel
, &otid
, InplaceUpdateTupleLock
);
17911 /* Update dependency on schema if caller said so */
17912 if (hasDependEntry
&&
17913 changeDependencyFor(RelationRelationId
,
17915 NamespaceRelationId
,
17918 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
17919 NameStr(classForm
->relname
));
17922 UnlockTuple(classRel
, &classTup
->t_self
, InplaceUpdateTupleLock
);
17925 add_exact_object_address(&thisobj
, objsMoved
);
17927 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
17930 heap_freetuple(classTup
);
17934 * Move all indexes for the specified relation to another namespace.
17936 * Note: we assume adequate permission checking was done by the caller,
17937 * and that the caller has a suitable lock on the owning relation.
17940 AlterIndexNamespaces(Relation classRel
, Relation rel
,
17941 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
17946 indexList
= RelationGetIndexList(rel
);
17948 foreach(l
, indexList
)
17950 Oid indexOid
= lfirst_oid(l
);
17951 ObjectAddress thisobj
;
17953 thisobj
.classId
= RelationRelationId
;
17954 thisobj
.objectId
= indexOid
;
17955 thisobj
.objectSubId
= 0;
17958 * Note: currently, the index will not have its own dependency on the
17959 * namespace, so we don't need to do changeDependencyFor(). There's no
17960 * row type in pg_type, either.
17962 * XXX this objsMoved test may be pointless -- surely we have a single
17963 * dependency link from a relation to each index?
17965 if (!object_address_present(&thisobj
, objsMoved
))
17967 AlterRelationNamespaceInternal(classRel
, indexOid
,
17968 oldNspOid
, newNspOid
,
17970 add_exact_object_address(&thisobj
, objsMoved
);
17974 list_free(indexList
);
17978 * Move all identity and SERIAL-column sequences of the specified relation to another
17981 * Note: we assume adequate permission checking was done by the caller,
17982 * and that the caller has a suitable lock on the owning relation.
17985 AlterSeqNamespaces(Relation classRel
, Relation rel
,
17986 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
17991 ScanKeyData key
[2];
17995 * SERIAL sequences are those having an auto dependency on one of the
17996 * table's columns (we don't care *which* column, exactly).
17998 depRel
= table_open(DependRelationId
, AccessShareLock
);
18000 ScanKeyInit(&key
[0],
18001 Anum_pg_depend_refclassid
,
18002 BTEqualStrategyNumber
, F_OIDEQ
,
18003 ObjectIdGetDatum(RelationRelationId
));
18004 ScanKeyInit(&key
[1],
18005 Anum_pg_depend_refobjid
,
18006 BTEqualStrategyNumber
, F_OIDEQ
,
18007 ObjectIdGetDatum(RelationGetRelid(rel
)));
18008 /* we leave refobjsubid unspecified */
18010 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
18013 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
18015 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
18018 /* skip dependencies other than auto dependencies on columns */
18019 if (depForm
->refobjsubid
== 0 ||
18020 depForm
->classid
!= RelationRelationId
||
18021 depForm
->objsubid
!= 0 ||
18022 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
18025 /* Use relation_open just in case it's an index */
18026 seqRel
= relation_open(depForm
->objid
, lockmode
);
18028 /* skip non-sequence relations */
18029 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
18031 /* No need to keep the lock */
18032 relation_close(seqRel
, lockmode
);
18036 /* Fix the pg_class and pg_depend entries */
18037 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
18038 oldNspOid
, newNspOid
,
18042 * Sequences used to have entries in pg_type, but no longer do. If we
18043 * ever re-instate that, we'll need to move the pg_type entry to the
18044 * new namespace, too (using AlterTypeNamespaceInternal).
18046 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
18048 /* Now we can close it. Keep the lock till end of transaction. */
18049 relation_close(seqRel
, NoLock
);
18052 systable_endscan(scan
);
18054 relation_close(depRel
, AccessShareLock
);
18059 * This code supports
18060 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18062 * Because we only support this for TEMP tables, it's sufficient to remember
18063 * the state in a backend-local data structure.
18067 * Register a newly-created relation's ON COMMIT action.
18070 register_on_commit_action(Oid relid
, OnCommitAction action
)
18073 MemoryContext oldcxt
;
18076 * We needn't bother registering the relation unless there is an ON COMMIT
18077 * action we need to take.
18079 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
18082 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
18084 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
18086 oc
->oncommit
= action
;
18087 oc
->creating_subid
= GetCurrentSubTransactionId();
18088 oc
->deleting_subid
= InvalidSubTransactionId
;
18091 * We use lcons() here so that ON COMMIT actions are processed in reverse
18092 * order of registration. That might not be essential but it seems
18095 on_commits
= lcons(oc
, on_commits
);
18097 MemoryContextSwitchTo(oldcxt
);
18101 * Unregister any ON COMMIT action when a relation is deleted.
18103 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18106 remove_on_commit_action(Oid relid
)
18110 foreach(l
, on_commits
)
18112 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18114 if (oc
->relid
== relid
)
18116 oc
->deleting_subid
= GetCurrentSubTransactionId();
18123 * Perform ON COMMIT actions.
18125 * This is invoked just before actually committing, since it's possible
18126 * to encounter errors.
18129 PreCommit_on_commit_actions(void)
18132 List
*oids_to_truncate
= NIL
;
18133 List
*oids_to_drop
= NIL
;
18135 foreach(l
, on_commits
)
18137 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18139 /* Ignore entry if already dropped in this xact */
18140 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
18143 switch (oc
->oncommit
)
18145 case ONCOMMIT_NOOP
:
18146 case ONCOMMIT_PRESERVE_ROWS
:
18147 /* Do nothing (there shouldn't be such entries, actually) */
18149 case ONCOMMIT_DELETE_ROWS
:
18152 * If this transaction hasn't accessed any temporary
18153 * relations, we can skip truncating ON COMMIT DELETE ROWS
18154 * tables, as they must still be empty.
18156 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
18157 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
18159 case ONCOMMIT_DROP
:
18160 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
18166 * Truncate relations before dropping so that all dependencies between
18167 * relations are removed after they are worked on. Doing it like this
18168 * might be a waste as it is possible that a relation being truncated will
18169 * be dropped anyway due to its parent being dropped, but this makes the
18170 * code more robust because of not having to re-check that the relation
18171 * exists at truncation time.
18173 if (oids_to_truncate
!= NIL
)
18174 heap_truncate(oids_to_truncate
);
18176 if (oids_to_drop
!= NIL
)
18178 ObjectAddresses
*targetObjects
= new_object_addresses();
18180 foreach(l
, oids_to_drop
)
18182 ObjectAddress object
;
18184 object
.classId
= RelationRelationId
;
18185 object
.objectId
= lfirst_oid(l
);
18186 object
.objectSubId
= 0;
18188 Assert(!object_address_present(&object
, targetObjects
));
18190 add_exact_object_address(&object
, targetObjects
);
18194 * Object deletion might involve toast table access (to clean up
18195 * toasted catalog entries), so ensure we have a valid snapshot.
18197 PushActiveSnapshot(GetTransactionSnapshot());
18200 * Since this is an automatic drop, rather than one directly initiated
18201 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18203 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
18204 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
18206 PopActiveSnapshot();
18208 #ifdef USE_ASSERT_CHECKING
18211 * Note that table deletion will call remove_on_commit_action, so the
18212 * entry should get marked as deleted.
18214 foreach(l
, on_commits
)
18216 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18218 if (oc
->oncommit
!= ONCOMMIT_DROP
)
18221 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
18228 * Post-commit or post-abort cleanup for ON COMMIT management.
18230 * All we do here is remove no-longer-needed OnCommitItem entries.
18232 * During commit, remove entries that were deleted during this transaction;
18233 * during abort, remove those created during this transaction.
18236 AtEOXact_on_commit_actions(bool isCommit
)
18238 ListCell
*cur_item
;
18240 foreach(cur_item
, on_commits
)
18242 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18244 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
18245 oc
->creating_subid
!= InvalidSubTransactionId
)
18247 /* cur_item must be removed */
18248 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18253 /* cur_item must be preserved */
18254 oc
->creating_subid
= InvalidSubTransactionId
;
18255 oc
->deleting_subid
= InvalidSubTransactionId
;
18261 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18263 * During subabort, we can immediately remove entries created during this
18264 * subtransaction. During subcommit, just relabel entries marked during
18265 * this subtransaction as being the parent's responsibility.
18268 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
18269 SubTransactionId parentSubid
)
18271 ListCell
*cur_item
;
18273 foreach(cur_item
, on_commits
)
18275 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18277 if (!isCommit
&& oc
->creating_subid
== mySubid
)
18279 /* cur_item must be removed */
18280 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18285 /* cur_item must be preserved */
18286 if (oc
->creating_subid
== mySubid
)
18287 oc
->creating_subid
= parentSubid
;
18288 if (oc
->deleting_subid
== mySubid
)
18289 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
18295 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18296 * the relation to be locked only if (1) it's a plain or partitioned table,
18297 * materialized view, or TOAST table and (2) the current user is the owner (or
18298 * the superuser) or has been granted MAINTAIN. This meets the
18299 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18300 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18303 RangeVarCallbackMaintainsTable(const RangeVar
*relation
,
18304 Oid relId
, Oid oldRelId
, void *arg
)
18307 AclResult aclresult
;
18309 /* Nothing to do if the relation was not found. */
18310 if (!OidIsValid(relId
))
18314 * If the relation does exist, check whether it's an index. But note that
18315 * the relation might have been dropped between the time we did the name
18316 * lookup and now. In that case, there's nothing to do.
18318 relkind
= get_rel_relkind(relId
);
18321 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
18322 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
18324 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18325 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
18327 /* Check permissions */
18328 aclresult
= pg_class_aclcheck(relId
, GetUserId(), ACL_MAINTAIN
);
18329 if (aclresult
!= ACLCHECK_OK
)
18330 aclcheck_error(aclresult
,
18331 get_relkind_objtype(get_rel_relkind(relId
)),
18332 relation
->relname
);
18336 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18339 RangeVarCallbackForTruncate(const RangeVar
*relation
,
18340 Oid relId
, Oid oldRelId
, void *arg
)
18344 /* Nothing to do if the relation was not found. */
18345 if (!OidIsValid(relId
))
18348 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18349 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18350 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18352 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18353 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18355 ReleaseSysCache(tuple
);
18359 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18360 * the owner of the relation, or superuser.
18363 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
18364 Oid relId
, Oid oldRelId
, void *arg
)
18368 /* Nothing to do if the relation was not found. */
18369 if (!OidIsValid(relId
))
18372 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18373 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18374 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18376 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
18377 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
18378 relation
->relname
);
18380 if (!allowSystemTableMods
&&
18381 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
18383 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18384 errmsg("permission denied: \"%s\" is a system catalog",
18385 relation
->relname
)));
18387 ReleaseSysCache(tuple
);
18391 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18395 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
18398 Node
*stmt
= (Node
*) arg
;
18399 ObjectType reltype
;
18401 Form_pg_class classform
;
18402 AclResult aclresult
;
18405 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
18406 if (!HeapTupleIsValid(tuple
))
18407 return; /* concurrently dropped */
18408 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
18409 relkind
= classform
->relkind
;
18411 /* Must own relation. */
18412 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
18413 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
18415 /* No system table modifications unless explicitly allowed. */
18416 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
18418 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18419 errmsg("permission denied: \"%s\" is a system catalog",
18423 * Extract the specified relation type from the statement parse tree.
18425 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18426 * have CREATE rights on the containing namespace.
18428 if (IsA(stmt
, RenameStmt
))
18430 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
18431 GetUserId(), ACL_CREATE
);
18432 if (aclresult
!= ACLCHECK_OK
)
18433 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
18434 get_namespace_name(classform
->relnamespace
));
18435 reltype
= ((RenameStmt
*) stmt
)->renameType
;
18437 else if (IsA(stmt
, AlterObjectSchemaStmt
))
18438 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
18440 else if (IsA(stmt
, AlterTableStmt
))
18441 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
18444 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
18445 reltype
= OBJECT_TABLE
; /* placate compiler */
18449 * For compatibility with prior releases, we allow ALTER TABLE to be used
18450 * with most other types of relations (but not composite types). We allow
18451 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18452 * otherwise. Otherwise, the user must select the correct form of the
18453 * command for the relation at issue.
18455 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
18457 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18458 errmsg("\"%s\" is not a sequence", rv
->relname
)));
18460 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
18462 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18463 errmsg("\"%s\" is not a view", rv
->relname
)));
18465 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
18467 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18468 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
18470 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
18472 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18473 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
18475 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
18477 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18478 errmsg("\"%s\" is not a composite type", rv
->relname
)));
18480 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
18481 relkind
!= RELKIND_PARTITIONED_INDEX
18482 && !IsA(stmt
, RenameStmt
))
18484 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18485 errmsg("\"%s\" is not an index", rv
->relname
)));
18488 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18491 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
18493 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18494 errmsg("\"%s\" is a composite type", rv
->relname
),
18495 /* translator: %s is an SQL ALTER command */
18496 errhint("Use %s instead.",
18500 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18501 * to a different schema, such as indexes and TOAST tables.
18503 if (IsA(stmt
, AlterObjectSchemaStmt
))
18505 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
18507 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18508 errmsg("cannot change schema of index \"%s\"",
18510 errhint("Change the schema of the table instead.")));
18511 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
18513 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18514 errmsg("cannot change schema of composite type \"%s\"",
18516 /* translator: %s is an SQL ALTER command */
18517 errhint("Use %s instead.",
18519 else if (relkind
== RELKIND_TOASTVALUE
)
18521 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18522 errmsg("cannot change schema of TOAST table \"%s\"",
18524 errhint("Change the schema of the table instead.")));
18527 ReleaseSysCache(tuple
);
18531 * Transform any expressions present in the partition key
18533 * Returns a transformed PartitionSpec.
18535 static PartitionSpec
*
18536 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
18538 PartitionSpec
*newspec
;
18539 ParseState
*pstate
;
18540 ParseNamespaceItem
*nsitem
;
18543 newspec
= makeNode(PartitionSpec
);
18545 newspec
->strategy
= partspec
->strategy
;
18546 newspec
->partParams
= NIL
;
18547 newspec
->location
= partspec
->location
;
18549 /* Check valid number of columns for strategy */
18550 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
18551 list_length(partspec
->partParams
) != 1)
18553 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18554 errmsg("cannot use \"list\" partition strategy with more than one column")));
18557 * Create a dummy ParseState and insert the target relation as its sole
18558 * rangetable entry. We need a ParseState for transformExpr.
18560 pstate
= make_parsestate(NULL
);
18561 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
18562 NULL
, false, true);
18563 addNSItemToQuery(pstate
, nsitem
, true, true, true);
18565 /* take care of any partition expressions */
18566 foreach(l
, partspec
->partParams
)
18568 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
18572 /* Copy, to avoid scribbling on the input */
18573 pelem
= copyObject(pelem
);
18575 /* Now do parse transformation of the expression */
18576 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
18577 EXPR_KIND_PARTITION_EXPRESSION
);
18579 /* we have to fix its collations too */
18580 assign_expr_collations(pstate
, pelem
->expr
);
18583 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
18590 * Compute per-partition-column information from a list of PartitionElems.
18591 * Expressions in the PartitionElems must be parse-analyzed already.
18594 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
18595 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
18596 PartitionStrategy strategy
)
18603 foreach(lc
, partParams
)
18605 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
18609 if (pelem
->name
!= NULL
)
18611 /* Simple attribute reference */
18612 HeapTuple atttuple
;
18613 Form_pg_attribute attform
;
18615 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
18617 if (!HeapTupleIsValid(atttuple
))
18619 (errcode(ERRCODE_UNDEFINED_COLUMN
),
18620 errmsg("column \"%s\" named in partition key does not exist",
18622 parser_errposition(pstate
, pelem
->location
)));
18623 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
18625 if (attform
->attnum
<= 0)
18627 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18628 errmsg("cannot use system column \"%s\" in partition key",
18630 parser_errposition(pstate
, pelem
->location
)));
18633 * Generated columns cannot work: They are computed after BEFORE
18634 * triggers, but partition routing is done before all triggers.
18636 if (attform
->attgenerated
)
18638 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18639 errmsg("cannot use generated column in partition key"),
18640 errdetail("Column \"%s\" is a generated column.",
18642 parser_errposition(pstate
, pelem
->location
)));
18644 partattrs
[attn
] = attform
->attnum
;
18645 atttype
= attform
->atttypid
;
18646 attcollation
= attform
->attcollation
;
18647 ReleaseSysCache(atttuple
);
18652 Node
*expr
= pelem
->expr
;
18653 char partattname
[16];
18655 Assert(expr
!= NULL
);
18656 atttype
= exprType(expr
);
18657 attcollation
= exprCollation(expr
);
18660 * The expression must be of a storable type (e.g., not RECORD).
18661 * The test is the same as for whether a table column is of a safe
18662 * type (which is why we needn't check for the non-expression
18665 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
18666 CheckAttributeType(partattname
,
18667 atttype
, attcollation
,
18668 NIL
, CHKATYPE_IS_PARTKEY
);
18671 * Strip any top-level COLLATE clause. This ensures that we treat
18672 * "x COLLATE y" and "(x COLLATE y)" alike.
18674 while (IsA(expr
, CollateExpr
))
18675 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
18677 if (IsA(expr
, Var
) &&
18678 ((Var
*) expr
)->varattno
> 0)
18681 * User wrote "(column)" or "(column COLLATE something)".
18682 * Treat it like simple attribute anyway.
18684 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
18688 Bitmapset
*expr_attrs
= NULL
;
18691 partattrs
[attn
] = 0; /* marks the column as expression */
18692 *partexprs
= lappend(*partexprs
, expr
);
18695 * transformPartitionSpec() should have already rejected
18696 * subqueries, aggregates, window functions, and SRFs, based
18697 * on the EXPR_KIND_ for partition expressions.
18701 * Cannot allow system column references, since that would
18702 * make partition routing impossible: their values won't be
18703 * known yet when we need to do that.
18705 pull_varattnos(expr
, 1, &expr_attrs
);
18706 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
18708 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
18711 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18712 errmsg("partition key expressions cannot contain system column references")));
18716 * Generated columns cannot work: They are computed after
18717 * BEFORE triggers, but partition routing is done before all
18721 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
18723 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
18726 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
18728 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18729 errmsg("cannot use generated column in partition key"),
18730 errdetail("Column \"%s\" is a generated column.",
18731 get_attname(RelationGetRelid(rel
), attno
, false)),
18732 parser_errposition(pstate
, pelem
->location
)));
18736 * Preprocess the expression before checking for mutability.
18737 * This is essential for the reasons described in
18738 * contain_mutable_functions_after_planning. However, we call
18739 * expression_planner for ourselves rather than using that
18740 * function, because if constant-folding reduces the
18741 * expression to a constant, we'd like to know that so we can
18744 * Like contain_mutable_functions_after_planning, assume that
18745 * expression_planner won't scribble on its input, so this
18746 * won't affect the partexprs entry we saved above.
18748 expr
= (Node
*) expression_planner((Expr
*) expr
);
18751 * Partition expressions cannot contain mutable functions,
18752 * because a given row must always map to the same partition
18753 * as long as there is no change in the partition boundary
18756 if (contain_mutable_functions(expr
))
18758 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18759 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18762 * While it is not exactly *wrong* for a partition expression
18763 * to be a constant, it seems better to reject such keys.
18765 if (IsA(expr
, Const
))
18767 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18768 errmsg("cannot use constant expression as partition key")));
18773 * Apply collation override if any
18775 if (pelem
->collation
)
18776 attcollation
= get_collation_oid(pelem
->collation
, false);
18779 * Check we have a collation iff it's a collatable type. The only
18780 * expected failures here are (1) COLLATE applied to a noncollatable
18781 * type, or (2) partition expression had an unresolved collation. But
18782 * we might as well code this to be a complete consistency check.
18784 if (type_is_collatable(atttype
))
18786 if (!OidIsValid(attcollation
))
18788 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
18789 errmsg("could not determine which collation to use for partition expression"),
18790 errhint("Use the COLLATE clause to set the collation explicitly.")));
18794 if (OidIsValid(attcollation
))
18796 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18797 errmsg("collations are not supported by type %s",
18798 format_type_be(atttype
))));
18801 partcollation
[attn
] = attcollation
;
18804 * Identify the appropriate operator class. For list and range
18805 * partitioning, we use a btree operator class; hash partitioning uses
18806 * a hash operator class.
18808 if (strategy
== PARTITION_STRATEGY_HASH
)
18809 am_oid
= HASH_AM_OID
;
18811 am_oid
= BTREE_AM_OID
;
18813 if (!pelem
->opclass
)
18815 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
18817 if (!OidIsValid(partopclass
[attn
]))
18819 if (strategy
== PARTITION_STRATEGY_HASH
)
18821 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18822 errmsg("data type %s has no default operator class for access method \"%s\"",
18823 format_type_be(atttype
), "hash"),
18824 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18827 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18828 errmsg("data type %s has no default operator class for access method \"%s\"",
18829 format_type_be(atttype
), "btree"),
18830 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18834 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
18836 am_oid
== HASH_AM_OID
? "hash" : "btree",
18844 * PartConstraintImpliedByRelConstraint
18845 * Do scanrel's existing constraints imply the partition constraint?
18847 * "Existing constraints" include its check constraints and column-level
18848 * not-null constraints. partConstraint describes the partition constraint,
18849 * in implicit-AND form.
18852 PartConstraintImpliedByRelConstraint(Relation scanrel
,
18853 List
*partConstraint
)
18855 List
*existConstraint
= NIL
;
18856 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18859 if (constr
&& constr
->has_not_null
)
18861 int natts
= scanrel
->rd_att
->natts
;
18863 for (i
= 1; i
<= natts
; i
++)
18865 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
18867 if (att
->attnotnull
&& !att
->attisdropped
)
18869 NullTest
*ntest
= makeNode(NullTest
);
18871 ntest
->arg
= (Expr
*) makeVar(1,
18877 ntest
->nulltesttype
= IS_NOT_NULL
;
18880 * argisrow=false is correct even for a composite column,
18881 * because attnotnull does not represent a SQL-spec IS NOT
18882 * NULL test in such a case, just IS DISTINCT FROM NULL.
18884 ntest
->argisrow
= false;
18885 ntest
->location
= -1;
18886 existConstraint
= lappend(existConstraint
, ntest
);
18891 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
18895 * ConstraintImpliedByRelConstraint
18896 * Do scanrel's existing constraints imply the given constraint?
18898 * testConstraint is the constraint to validate. provenConstraint is a
18899 * caller-provided list of conditions which this function may assume
18900 * to be true. Both provenConstraint and testConstraint must be in
18901 * implicit-AND form, must only contain immutable clauses, and must
18902 * contain only Vars with varno = 1.
18905 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
18907 List
*existConstraint
= list_copy(provenConstraint
);
18908 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18912 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
18913 for (i
= 0; i
< num_check
; i
++)
18918 * If this constraint hasn't been fully validated yet, we must ignore
18921 if (!constr
->check
[i
].ccvalid
)
18925 * NOT ENFORCED constraints are always marked as invalid, which should
18926 * have been ignored.
18928 Assert(constr
->check
[i
].ccenforced
);
18930 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
18933 * Run each expression through const-simplification and
18934 * canonicalization. It is necessary, because we will be comparing it
18935 * to similarly-processed partition constraint expressions, and may
18936 * fail to detect valid matches without this.
18938 cexpr
= eval_const_expressions(NULL
, cexpr
);
18939 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
18941 existConstraint
= list_concat(existConstraint
,
18942 make_ands_implicit((Expr
*) cexpr
));
18946 * Try to make the proof. Since we are comparing CHECK constraints, we
18947 * need to use weak implication, i.e., we assume existConstraint is
18948 * not-false and try to prove the same for testConstraint.
18950 * Note that predicate_implied_by assumes its first argument is known
18951 * immutable. That should always be true for both NOT NULL and partition
18952 * constraints, so we don't test it here.
18954 return predicate_implied_by(testConstraint
, existConstraint
, true);
18958 * QueuePartitionConstraintValidation
18960 * Add an entry to wqueue to have the given partition constraint validated by
18961 * Phase 3, for the given relation, and all its children.
18963 * We first verify whether the given constraint is implied by pre-existing
18964 * relation constraints; if it is, there's no need to scan the table to
18965 * validate, so don't queue in that case.
18968 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
18969 List
*partConstraint
,
18970 bool validate_default
)
18973 * Based on the table's existing constraints, determine whether or not we
18974 * may skip scanning the table.
18976 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
18978 if (!validate_default
)
18980 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18981 RelationGetRelationName(scanrel
))));
18984 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18985 RelationGetRelationName(scanrel
))));
18990 * Constraints proved insufficient. For plain relations, queue a
18991 * validation item now; for partitioned tables, recurse to process each
18994 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
18996 AlteredTableInfo
*tab
;
18998 /* Grab a work queue entry. */
18999 tab
= ATGetQueueEntry(wqueue
, scanrel
);
19000 Assert(tab
->partition_constraint
== NULL
);
19001 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
19002 tab
->validate_default
= validate_default
;
19004 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19006 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
19009 for (i
= 0; i
< partdesc
->nparts
; i
++)
19012 List
*thisPartConstraint
;
19015 * This is the minimum lock we need to prevent deadlocks.
19017 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
19020 * Adjust the constraint for scanrel so that it matches this
19021 * partition's attribute numbers.
19023 thisPartConstraint
=
19024 map_partition_varattnos(partConstraint
, 1,
19025 part_rel
, scanrel
);
19027 QueuePartitionConstraintValidation(wqueue
, part_rel
,
19028 thisPartConstraint
,
19030 table_close(part_rel
, NoLock
); /* keep lock till commit */
19036 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19038 * Return the address of the newly attached partition.
19040 static ObjectAddress
19041 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
19042 AlterTableUtilityContext
*context
)
19044 Relation attachrel
,
19046 List
*attachrel_children
;
19047 List
*partConstraint
;
19052 TupleDesc tupleDesc
;
19053 ObjectAddress address
;
19054 const char *trigger_name
;
19055 Oid defaultPartOid
;
19056 List
*partBoundConstraint
;
19057 ParseState
*pstate
= make_parsestate(NULL
);
19059 pstate
->p_sourcetext
= context
->queryString
;
19062 * We must lock the default partition if one exists, because attaching a
19063 * new partition will change its partition constraint.
19066 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19067 if (OidIsValid(defaultPartOid
))
19068 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19070 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
19073 * XXX I think it'd be a good idea to grab locks on all tables referenced
19074 * by FKs at this point also.
19078 * Must be owner of both parent and source table -- parent was checked by
19079 * ATSimplePermissions call in ATPrepCmd
19081 ATSimplePermissions(AT_AttachPartition
, attachrel
,
19082 ATT_TABLE
| ATT_PARTITIONED_TABLE
| ATT_FOREIGN_TABLE
);
19084 /* A partition can only have one parent */
19085 if (attachrel
->rd_rel
->relispartition
)
19087 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19088 errmsg("\"%s\" is already a partition",
19089 RelationGetRelationName(attachrel
))));
19091 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
19093 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19094 errmsg("cannot attach a typed table as partition")));
19097 * Table being attached should not already be part of inheritance; either
19098 * as a child table...
19100 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
19102 Anum_pg_inherits_inhrelid
,
19103 BTEqualStrategyNumber
, F_OIDEQ
,
19104 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19105 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
19107 if (HeapTupleIsValid(systable_getnext(scan
)))
19109 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19110 errmsg("cannot attach inheritance child as partition")));
19111 systable_endscan(scan
);
19113 /* ...or as a parent table (except the case when it is partitioned) */
19115 Anum_pg_inherits_inhparent
,
19116 BTEqualStrategyNumber
, F_OIDEQ
,
19117 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19118 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
19120 if (HeapTupleIsValid(systable_getnext(scan
)) &&
19121 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
19123 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19124 errmsg("cannot attach inheritance parent as partition")));
19125 systable_endscan(scan
);
19126 table_close(catalog
, AccessShareLock
);
19129 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19130 * particular, this disallows making a rel a partition of itself.)
19132 * We do that by checking if rel is a member of the list of attachrel's
19133 * partitions provided the latter is partitioned at all. We want to avoid
19134 * having to construct this list again, so we request the strongest lock
19135 * on all partitions. We need the strongest lock, because we may decide
19136 * to scan them if we find out that the table being attached (or its leaf
19137 * partitions) may contain rows that violate the partition constraint. If
19138 * the table has a constraint that would prevent such rows, which by
19139 * definition is present in all the partitions, we need not scan the
19140 * table, nor its partitions. But we cannot risk a deadlock by taking a
19141 * weaker lock now and the stronger one only when needed.
19143 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
19144 AccessExclusiveLock
, NULL
);
19145 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
19147 (errcode(ERRCODE_DUPLICATE_TABLE
),
19148 errmsg("circular inheritance not allowed"),
19149 errdetail("\"%s\" is already a child of \"%s\".",
19150 RelationGetRelationName(rel
),
19151 RelationGetRelationName(attachrel
))));
19153 /* If the parent is permanent, so must be all of its partitions. */
19154 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
19155 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
19157 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19158 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19159 RelationGetRelationName(rel
))));
19161 /* Temp parent cannot have a partition that is itself not a temp */
19162 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19163 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
19165 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19166 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19167 RelationGetRelationName(rel
))));
19169 /* If the parent is temp, it must belong to this session */
19170 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19171 !rel
->rd_islocaltemp
)
19173 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19174 errmsg("cannot attach as partition of temporary relation of another session")));
19176 /* Ditto for the partition */
19177 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19178 !attachrel
->rd_islocaltemp
)
19180 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19181 errmsg("cannot attach temporary relation of another session as partition")));
19184 * Check if attachrel has any identity columns or any columns that aren't
19187 tupleDesc
= RelationGetDescr(attachrel
);
19188 natts
= tupleDesc
->natts
;
19189 for (attno
= 1; attno
<= natts
; attno
++)
19191 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
19192 char *attributeName
= NameStr(attribute
->attname
);
19194 /* Ignore dropped */
19195 if (attribute
->attisdropped
)
19198 if (attribute
->attidentity
)
19200 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19201 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19202 RelationGetRelationName(attachrel
), attributeName
),
19203 errdetail("The new partition may not contain an identity column."));
19205 /* Try to find the column in parent (matching on column name) */
19206 if (!SearchSysCacheExists2(ATTNAME
,
19207 ObjectIdGetDatum(RelationGetRelid(rel
)),
19208 CStringGetDatum(attributeName
)))
19210 (errcode(ERRCODE_DATATYPE_MISMATCH
),
19211 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19212 RelationGetRelationName(attachrel
), attributeName
,
19213 RelationGetRelationName(rel
)),
19214 errdetail("The new partition may contain only the columns present in parent.")));
19218 * If child_rel has row-level triggers with transition tables, we
19219 * currently don't allow it to become a partition. See also prohibitions
19220 * in ATExecAddInherit() and CreateTrigger().
19222 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
19223 if (trigger_name
!= NULL
)
19225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
19226 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19227 trigger_name
, RelationGetRelationName(attachrel
)),
19228 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19231 * Check that the new partition's bound is valid and does not overlap any
19232 * of existing partitions of the parent - note that it does not return on
19235 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
19236 cmd
->bound
, pstate
);
19238 /* OK to create inheritance. Rest of the checks performed there */
19239 CreateInheritance(attachrel
, rel
, true);
19241 /* Update the pg_class entry. */
19242 StorePartitionBound(attachrel
, rel
, cmd
->bound
);
19244 /* Ensure there exists a correct set of indexes in the partition. */
19245 AttachPartitionEnsureIndexes(wqueue
, rel
, attachrel
);
19248 CloneRowTriggersToPartition(rel
, attachrel
);
19251 * Clone foreign key constraints. Callee is responsible for setting up
19252 * for phase 3 constraint verification.
19254 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
19257 * Generate partition constraint from the partition bound specification.
19258 * If the parent itself is a partition, make sure to include its
19259 * constraint as well.
19261 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
19264 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19265 * since it's needed later to construct the constraint expression for
19266 * validating against the default partition, if any.
19268 partConstraint
= list_concat_copy(partBoundConstraint
,
19269 RelationGetPartitionQual(rel
));
19271 /* Skip validation if there are no constraints to validate. */
19272 if (partConstraint
)
19275 * Run the partition quals through const-simplification similar to
19276 * check constraints. We skip canonicalize_qual, though, because
19277 * partition quals should be in canonical form already.
19280 (List
*) eval_const_expressions(NULL
,
19281 (Node
*) partConstraint
);
19283 /* XXX this sure looks wrong */
19284 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
19287 * Adjust the generated constraint to match this partition's attribute
19290 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
19293 /* Validate partition constraints against the table being attached. */
19294 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
19299 * If we're attaching a partition other than the default partition and a
19300 * default one exists, then that partition's partition constraint changes,
19301 * so add an entry to the work queue to validate it, too. (We must not do
19302 * this when the partition being attached is the default one; we already
19305 if (OidIsValid(defaultPartOid
))
19307 Relation defaultrel
;
19308 List
*defPartConstraint
;
19310 Assert(!cmd
->bound
->is_default
);
19312 /* we already hold a lock on the default partition */
19313 defaultrel
= table_open(defaultPartOid
, NoLock
);
19314 defPartConstraint
=
19315 get_proposed_default_constraint(partBoundConstraint
);
19318 * Map the Vars in the constraint expression from rel's attnos to
19321 defPartConstraint
=
19322 map_partition_varattnos(defPartConstraint
,
19323 1, defaultrel
, rel
);
19324 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
19325 defPartConstraint
, true);
19327 /* keep our lock until commit. */
19328 table_close(defaultrel
, NoLock
);
19331 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
19334 * If the partition we just attached is partitioned itself, invalidate
19335 * relcache for all descendent partitions too to ensure that their
19336 * rd_partcheck expression trees are rebuilt; partitions already locked at
19337 * the beginning of this function.
19339 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19343 foreach(l
, attachrel_children
)
19345 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
19349 /* keep our lock until commit */
19350 table_close(attachrel
, NoLock
);
19356 * AttachPartitionEnsureIndexes
19357 * subroutine for ATExecAttachPartition to create/match indexes
19359 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19360 * PARTITION: every partition must have an index attached to each index on the
19361 * partitioned table.
19364 AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
)
19367 List
*attachRelIdxs
;
19368 Relation
*attachrelIdxRels
;
19369 IndexInfo
**attachInfos
;
19372 MemoryContext oldcxt
;
19374 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
19375 "AttachPartitionEnsureIndexes",
19376 ALLOCSET_DEFAULT_SIZES
);
19377 oldcxt
= MemoryContextSwitchTo(cxt
);
19379 idxes
= RelationGetIndexList(rel
);
19380 attachRelIdxs
= RelationGetIndexList(attachrel
);
19381 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
19382 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
19384 /* Build arrays of all existing indexes and their IndexInfos */
19385 foreach_oid(cldIdxId
, attachRelIdxs
)
19387 int i
= foreach_current_index(cldIdxId
);
19389 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
19390 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
19394 * If we're attaching a foreign table, we must fail if any of the indexes
19395 * is a constraint index; otherwise, there's nothing to do here. Do this
19396 * before starting work, to avoid wasting the effort of building a few
19397 * non-unique indexes before coming across a unique one.
19399 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
19401 foreach(cell
, idxes
)
19403 Oid idx
= lfirst_oid(cell
);
19404 Relation idxRel
= index_open(idx
, AccessShareLock
);
19406 if (idxRel
->rd_index
->indisunique
||
19407 idxRel
->rd_index
->indisprimary
)
19409 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19410 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19411 RelationGetRelationName(attachrel
),
19412 RelationGetRelationName(rel
)),
19413 errdetail("Partitioned table \"%s\" contains unique indexes.",
19414 RelationGetRelationName(rel
))));
19415 index_close(idxRel
, AccessShareLock
);
19422 * For each index on the partitioned table, find a matching one in the
19423 * partition-to-be; if one is not found, create one.
19425 foreach(cell
, idxes
)
19427 Oid idx
= lfirst_oid(cell
);
19428 Relation idxRel
= index_open(idx
, AccessShareLock
);
19431 bool found
= false;
19435 * Ignore indexes in the partitioned table other than partitioned
19438 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
19440 index_close(idxRel
, AccessShareLock
);
19444 /* construct an indexinfo to compare existing indexes against */
19445 info
= BuildIndexInfo(idxRel
);
19446 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
19447 RelationGetDescr(rel
),
19449 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
19452 * Scan the list of existing indexes in the partition-to-be, and mark
19453 * the first matching, valid, unattached one we find, if any, as
19454 * partition of the parent index. If we find one, we're done.
19456 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19458 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
19459 Oid cldConstrOid
= InvalidOid
;
19461 /* does this index have a parent? if so, can't use it */
19462 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
19465 /* If this index is invalid, can't use it */
19466 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
19469 if (CompareIndexInfo(attachInfos
[i
], info
,
19470 attachrelIdxRels
[i
]->rd_indcollation
,
19471 idxRel
->rd_indcollation
,
19472 attachrelIdxRels
[i
]->rd_opfamily
,
19473 idxRel
->rd_opfamily
,
19477 * If this index is being created in the parent because of a
19478 * constraint, then the child needs to have a constraint also,
19479 * so look for one. If there is no such constraint, this
19480 * index is no good, so keep looking.
19482 if (OidIsValid(constraintOid
))
19485 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
19488 if (!OidIsValid(cldConstrOid
))
19491 /* Ensure they're both the same type of constraint */
19492 if (get_constraint_type(constraintOid
) !=
19493 get_constraint_type(cldConstrOid
))
19498 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
19499 if (OidIsValid(constraintOid
))
19500 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
19501 RelationGetRelid(attachrel
));
19504 CommandCounterIncrement();
19510 * If no suitable index was found in the partition-to-be, create one
19511 * now. Note that if this is a PK, not-null constraints must already
19519 stmt
= generateClonedIndexStmt(NULL
,
19522 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
19523 RelationGetRelid(idxRel
),
19526 true, false, false, false, false);
19529 index_close(idxRel
, AccessShareLock
);
19534 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19535 index_close(attachrelIdxRels
[i
], AccessShareLock
);
19536 MemoryContextSwitchTo(oldcxt
);
19537 MemoryContextDelete(cxt
);
19541 * CloneRowTriggersToPartition
19542 * subroutine for ATExecAttachPartition/DefineRelation to create row
19543 * triggers on partitions
19546 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
19548 Relation pg_trigger
;
19552 MemoryContext perTupCxt
;
19554 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19555 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
19556 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
19557 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
19558 true, NULL
, 1, &key
);
19560 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
19561 "clone trig", ALLOCSET_SMALL_SIZES
);
19563 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
19565 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
19566 CreateTrigStmt
*trigStmt
;
19571 List
*trigargs
= NIL
;
19572 MemoryContext oldcxt
;
19575 * Ignore statement-level triggers; those are not cloned.
19577 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
19581 * Don't clone internal triggers, because the constraint cloning code
19584 if (trigForm
->tgisinternal
)
19588 * Complain if we find an unexpected trigger type.
19590 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
19591 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
19592 elog(ERROR
, "unexpected trigger \"%s\" found",
19593 NameStr(trigForm
->tgname
));
19595 /* Use short-lived context for CREATE TRIGGER */
19596 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
19599 * If there is a WHEN clause, generate a 'cooked' version of it that's
19600 * appropriate for the partition.
19602 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
19603 RelationGetDescr(pg_trigger
), &isnull
);
19606 qual
= stringToNode(TextDatumGetCString(value
));
19607 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
19608 partition
, parent
);
19609 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
19610 partition
, parent
);
19614 * If there is a column list, transform it to a list of column names.
19615 * Note we don't need to map this list in any way ...
19617 if (trigForm
->tgattr
.dim1
> 0)
19621 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
19623 Form_pg_attribute col
;
19625 col
= TupleDescAttr(parent
->rd_att
,
19626 trigForm
->tgattr
.values
[i
] - 1);
19627 cols
= lappend(cols
,
19628 makeString(pstrdup(NameStr(col
->attname
))));
19632 /* Reconstruct trigger arguments list. */
19633 if (trigForm
->tgnargs
> 0)
19637 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
19638 RelationGetDescr(pg_trigger
), &isnull
);
19640 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19641 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
19643 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
19645 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
19647 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
19648 p
+= strlen(p
) + 1;
19652 trigStmt
= makeNode(CreateTrigStmt
);
19653 trigStmt
->replace
= false;
19654 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
19655 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
19656 trigStmt
->relation
= NULL
;
19657 trigStmt
->funcname
= NULL
; /* passed separately */
19658 trigStmt
->args
= trigargs
;
19659 trigStmt
->row
= true;
19660 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
19661 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
19662 trigStmt
->columns
= cols
;
19663 trigStmt
->whenClause
= NULL
; /* passed separately */
19664 trigStmt
->transitionRels
= NIL
; /* not supported at present */
19665 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
19666 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
19667 trigStmt
->constrrel
= NULL
; /* passed separately */
19669 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
19670 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
19671 trigForm
->tgfoid
, trigForm
->oid
, qual
,
19672 false, true, trigForm
->tgenabled
);
19674 MemoryContextSwitchTo(oldcxt
);
19675 MemoryContextReset(perTupCxt
);
19678 MemoryContextDelete(perTupCxt
);
19680 systable_endscan(scan
);
19681 table_close(pg_trigger
, RowExclusiveLock
);
19685 * ALTER TABLE DETACH PARTITION
19687 * Return the address of the relation that is no longer a partition of rel.
19689 * If concurrent mode is requested, we run in two transactions. A side-
19690 * effect is that this command cannot run in a multi-part ALTER TABLE.
19691 * Currently, that's enforced by the grammar.
19693 * The strategy for concurrency is to first modify the partition's
19694 * pg_inherit catalog row to make it visible to everyone that the
19695 * partition is detached, lock the partition against writes, and commit
19696 * the transaction; anyone who requests the partition descriptor from
19697 * that point onwards has to ignore such a partition. In a second
19698 * transaction, we wait until all transactions that could have seen the
19699 * partition as attached are gone, then we remove the rest of partition
19700 * metadata (pg_inherits and pg_class.relpartbounds).
19702 static ObjectAddress
19703 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
19704 RangeVar
*name
, bool concurrent
)
19707 ObjectAddress address
;
19708 Oid defaultPartOid
;
19711 * We must lock the default partition, because detaching this partition
19712 * will change its partition constraint.
19715 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19716 if (OidIsValid(defaultPartOid
))
19719 * Concurrent detaching when a default partition exists is not
19720 * supported. The main problem is that the default partition
19721 * constraint would change. And there's a definitional problem: what
19722 * should happen to the tuples that are being inserted that belong to
19723 * the partition being detached? Putting them on the partition being
19724 * detached would be wrong, since they'd become "lost" after the
19725 * detaching completes but we cannot put them in the default partition
19726 * either until we alter its partition constraint.
19728 * I think we could solve this problem if we effected the constraint
19729 * change before committing the first transaction. But the lock would
19730 * have to remain AEL and it would cause concurrent query planning to
19731 * be blocked, so changing it that way would be even worse.
19735 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19736 errmsg("cannot detach partitions concurrently when a default partition exists")));
19737 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19741 * In concurrent mode, the partition is locked with share-update-exclusive
19742 * in the first transaction. This allows concurrent transactions to be
19743 * doing DML to the partition.
19745 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
19746 AccessExclusiveLock
);
19749 * Check inheritance conditions and either delete the pg_inherits row (in
19750 * non-concurrent mode) or just set the inhdetachpending flag.
19753 RemoveInheritance(partRel
, rel
, false);
19755 MarkInheritDetached(partRel
, rel
);
19758 * Ensure that foreign keys still hold after this detach. This keeps
19759 * locks on the referencing tables, which prevents concurrent transactions
19760 * from adding rows that we wouldn't see. For this to work in concurrent
19761 * mode, it is critical that the partition appears as no longer attached
19762 * for the RI queries as soon as the first transaction commits.
19764 ATDetachCheckNoForeignKeyRefs(partRel
);
19767 * Concurrent mode has to work harder; first we add a new constraint to
19768 * the partition that matches the partition constraint. Then we close our
19769 * existing transaction, and in a new one wait for all processes to catch
19770 * up on the catalog updates we've done so far; at that point we can
19771 * complete the operation.
19778 char *parentrelname
;
19782 * Add a new constraint to the partition being detached, which
19783 * supplants the partition constraint (unless there is one already).
19785 DetachAddConstraintIfNeeded(wqueue
, partRel
);
19788 * We're almost done now; the only traces that remain are the
19789 * pg_inherits tuple and the partition's relpartbounds. Before we can
19790 * remove those, we need to wait until all transactions that know that
19791 * this is a partition are gone.
19795 * Remember relation OIDs to re-acquire them later; and relation names
19796 * too, for error messages if something is dropped in between.
19798 partrelid
= RelationGetRelid(partRel
);
19799 parentrelid
= RelationGetRelid(rel
);
19800 parentrelname
= MemoryContextStrdup(PortalContext
,
19801 RelationGetRelationName(rel
));
19802 partrelname
= MemoryContextStrdup(PortalContext
,
19803 RelationGetRelationName(partRel
));
19805 /* Invalidate relcache entries for the parent -- must be before close */
19806 CacheInvalidateRelcache(rel
);
19808 table_close(partRel
, NoLock
);
19809 table_close(rel
, NoLock
);
19812 /* Make updated catalog entry visible */
19813 PopActiveSnapshot();
19814 CommitTransactionCommand();
19816 StartTransactionCommand();
19819 * Now wait. This ensures that all queries that were planned
19820 * including the partition are finished before we remove the rest of
19821 * catalog entries. We don't need or indeed want to acquire this
19822 * lock, though -- that would block later queries.
19824 * We don't need to concern ourselves with waiting for a lock on the
19825 * partition itself, since we will acquire AccessExclusiveLock below.
19827 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
19828 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
19831 * Now acquire locks in both relations again. Note they may have been
19832 * removed in the meantime, so care is required.
19834 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
19835 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
19837 /* If the relations aren't there, something bad happened; bail out */
19840 if (partRel
!= NULL
) /* shouldn't happen */
19841 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
19844 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19845 errmsg("partitioned table \"%s\" was removed concurrently",
19848 if (partRel
== NULL
)
19850 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19851 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
19856 /* Do the final part of detaching */
19857 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
19859 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19861 /* keep our lock until commit */
19862 table_close(partRel
, NoLock
);
19868 * Second part of ALTER TABLE .. DETACH.
19870 * This is separate so that it can be run independently when the second
19871 * transaction of the concurrent algorithm fails (crash or abort).
19874 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
19875 Oid defaultPartOid
)
19881 Datum new_val
[Natts_pg_class
];
19882 bool new_null
[Natts_pg_class
],
19883 new_repl
[Natts_pg_class
];
19886 Relation trigrel
= NULL
;
19891 * We can remove the pg_inherits row now. (In the non-concurrent case,
19892 * this was already done).
19894 RemoveInheritance(partRel
, rel
, true);
19897 /* Drop any triggers that were cloned on creation/attach. */
19898 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
19901 * Detach any foreign keys that are inherited. This includes creating
19902 * additional action triggers.
19904 fks
= copyObject(RelationGetFKeyList(partRel
));
19906 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19909 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
19911 Form_pg_constraint conform
;
19912 Oid insertTriggerOid
,
19915 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
19916 if (!HeapTupleIsValid(contup
))
19917 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
19918 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
19920 /* consider only the inherited foreign keys */
19921 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
19922 !OidIsValid(conform
->conparentid
))
19924 ReleaseSysCache(contup
);
19929 * The constraint on this table must be marked no longer a child of
19930 * the parent's constraint, as do its check triggers.
19932 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
19935 * Also, look up the partition's "check" triggers corresponding to the
19936 * constraint being detached and detach them from the parent triggers.
19938 GetForeignKeyCheckTriggers(trigrel
,
19939 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
19940 &insertTriggerOid
, &updateTriggerOid
);
19941 Assert(OidIsValid(insertTriggerOid
));
19942 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
19943 RelationGetRelid(partRel
));
19944 Assert(OidIsValid(updateTriggerOid
));
19945 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
19946 RelationGetRelid(partRel
));
19949 * Lastly, create the action triggers on the referenced table, using
19950 * addFkRecurseReferenced, which requires some elaborate setup (so put
19951 * it in a separate block). While at it, if the table is partitioned,
19952 * that function will recurse to create the pg_constraint rows and
19953 * action triggers for each partition.
19955 * Note there's no need to do addFkConstraint() here, because the
19956 * pg_constraint row already exists.
19959 Constraint
*fkconstraint
;
19961 AttrNumber conkey
[INDEX_MAX_KEYS
];
19962 AttrNumber confkey
[INDEX_MAX_KEYS
];
19963 Oid conpfeqop
[INDEX_MAX_KEYS
];
19964 Oid conppeqop
[INDEX_MAX_KEYS
];
19965 Oid conffeqop
[INDEX_MAX_KEYS
];
19966 int numfkdelsetcols
;
19967 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
19970 DeconstructFkConstraintRow(contup
,
19980 /* Create a synthetic node we'll use throughout */
19981 fkconstraint
= makeNode(Constraint
);
19982 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
19983 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
19984 fkconstraint
->deferrable
= conform
->condeferrable
;
19985 fkconstraint
->initdeferred
= conform
->condeferred
;
19986 fkconstraint
->skip_validation
= true;
19987 fkconstraint
->initially_valid
= true;
19988 /* a few irrelevant fields omitted here */
19989 fkconstraint
->pktable
= NULL
;
19990 fkconstraint
->fk_attrs
= NIL
;
19991 fkconstraint
->pk_attrs
= NIL
;
19992 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
19993 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
19994 fkconstraint
->fk_del_action
= conform
->confdeltype
;
19995 fkconstraint
->fk_del_set_cols
= NIL
;
19996 fkconstraint
->old_conpfeqop
= NIL
;
19997 fkconstraint
->old_pktable_oid
= InvalidOid
;
19998 fkconstraint
->location
= -1;
20000 /* set up colnames, used to generate the constraint name */
20001 for (int i
= 0; i
< numfks
; i
++)
20003 Form_pg_attribute att
;
20005 att
= TupleDescAttr(RelationGetDescr(partRel
),
20008 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
20009 makeString(NameStr(att
->attname
)));
20012 refdRel
= table_open(fk
->confrelid
, ShareRowExclusiveLock
);
20014 addFkRecurseReferenced(fkconstraint
, partRel
,
20027 InvalidOid
, InvalidOid
,
20028 conform
->conperiod
);
20029 table_close(refdRel
, NoLock
); /* keep lock till end of xact */
20032 ReleaseSysCache(contup
);
20034 list_free_deep(fks
);
20036 table_close(trigrel
, RowExclusiveLock
);
20039 * Any sub-constraints that are in the referenced-side of a larger
20040 * constraint have to be removed. This partition is no longer part of the
20041 * key space of the constraint.
20043 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
20045 Oid constrOid
= lfirst_oid(cell
);
20046 ObjectAddress constraint
;
20048 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20049 deleteDependencyRecordsForClass(ConstraintRelationId
,
20051 ConstraintRelationId
,
20052 DEPENDENCY_INTERNAL
);
20053 CommandCounterIncrement();
20055 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
20056 performDeletion(&constraint
, DROP_RESTRICT
, 0);
20059 /* Now we can detach indexes */
20060 indexes
= RelationGetIndexList(partRel
);
20061 foreach(cell
, indexes
)
20063 Oid idxid
= lfirst_oid(cell
);
20067 Oid parentConstrOid
;
20069 if (!has_superclass(idxid
))
20072 parentidx
= get_partition_parent(idxid
, false);
20073 Assert((IndexGetRelation(parentidx
, false) == RelationGetRelid(rel
)));
20075 idx
= index_open(idxid
, AccessExclusiveLock
);
20076 IndexSetParentIndex(idx
, InvalidOid
);
20079 * If there's a constraint associated with the index, detach it too.
20080 * Careful: it is possible for a constraint index in a partition to be
20081 * the child of a non-constraint index, so verify whether the parent
20082 * index does actually have a constraint.
20084 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
20086 parentConstrOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
),
20088 if (OidIsValid(parentConstrOid
) && OidIsValid(constrOid
))
20089 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20091 index_close(idx
, NoLock
);
20094 /* Update pg_class tuple */
20095 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
20096 tuple
= SearchSysCacheCopy1(RELOID
,
20097 ObjectIdGetDatum(RelationGetRelid(partRel
)));
20098 if (!HeapTupleIsValid(tuple
))
20099 elog(ERROR
, "cache lookup failed for relation %u",
20100 RelationGetRelid(partRel
));
20101 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
20103 /* Clear relpartbound and reset relispartition */
20104 memset(new_val
, 0, sizeof(new_val
));
20105 memset(new_null
, false, sizeof(new_null
));
20106 memset(new_repl
, false, sizeof(new_repl
));
20107 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
20108 new_null
[Anum_pg_class_relpartbound
- 1] = true;
20109 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
20110 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
20111 new_val
, new_null
, new_repl
);
20113 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
20114 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
20115 heap_freetuple(newtuple
);
20116 table_close(classRel
, RowExclusiveLock
);
20119 * Drop identity property from all identity columns of partition.
20121 for (int attno
= 0; attno
< RelationGetNumberOfAttributes(partRel
); attno
++)
20123 Form_pg_attribute attr
= TupleDescAttr(partRel
->rd_att
, attno
);
20125 if (!attr
->attisdropped
&& attr
->attidentity
)
20126 ATExecDropIdentity(partRel
, NameStr(attr
->attname
), false,
20127 AccessExclusiveLock
, true, true);
20130 if (OidIsValid(defaultPartOid
))
20133 * If the relation being detached is the default partition itself,
20134 * remove it from the parent's pg_partitioned_table entry.
20136 * If not, we must invalidate default partition's relcache entry, as
20137 * in StorePartitionBound: its partition constraint depends on every
20138 * other partition's partition constraint.
20140 if (RelationGetRelid(partRel
) == defaultPartOid
)
20141 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
20143 CacheInvalidateRelcacheByRelid(defaultPartOid
);
20147 * Invalidate the parent's relcache so that the partition is no longer
20148 * included in its partition descriptor.
20150 CacheInvalidateRelcache(rel
);
20153 * If the partition we just detached is partitioned itself, invalidate
20154 * relcache for all descendent partitions too to ensure that their
20155 * rd_partcheck expression trees are rebuilt; must lock partitions before
20156 * doing so, using the same lockmode as what partRel has been locked with
20159 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
20163 children
= find_all_inheritors(RelationGetRelid(partRel
),
20164 AccessExclusiveLock
, NULL
);
20165 foreach(cell
, children
)
20167 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
20173 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20175 * To use when a DETACH PARTITION command previously did not run to
20176 * completion; this completes the detaching process.
20178 static ObjectAddress
20179 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
20182 ObjectAddress address
;
20183 Snapshot snap
= GetActiveSnapshot();
20185 partRel
= table_openrv(name
, AccessExclusiveLock
);
20188 * Wait until existing snapshots are gone. This is important if the
20189 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20190 * user could immediately run DETACH FINALIZE without actually waiting for
20191 * existing transactions. We must not complete the detach action until
20192 * all such queries are complete (otherwise we would present them with an
20193 * inconsistent view of catalogs).
20195 WaitForOlderSnapshots(snap
->xmin
, false);
20197 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
20199 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
20201 table_close(partRel
, NoLock
);
20207 * DetachAddConstraintIfNeeded
20208 * Subroutine for ATExecDetachPartition. Create a constraint that
20209 * takes the place of the partition constraint, but avoid creating
20210 * a dupe if a constraint already exists which implies the needed
20214 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
20216 List
*constraintExpr
;
20218 constraintExpr
= RelationGetPartitionQual(partRel
);
20219 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
20222 * Avoid adding a new constraint if the needed constraint is implied by an
20223 * existing constraint
20225 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
20227 AlteredTableInfo
*tab
;
20230 tab
= ATGetQueueEntry(wqueue
, partRel
);
20232 /* Add constraint on partition, equivalent to the partition constraint */
20233 n
= makeNode(Constraint
);
20234 n
->contype
= CONSTR_CHECK
;
20237 n
->is_no_inherit
= false;
20238 n
->raw_expr
= NULL
;
20239 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
20240 n
->is_enforced
= true;
20241 n
->initially_valid
= true;
20242 n
->skip_validation
= true;
20243 /* It's a re-add, since it nominally already exists */
20244 ATAddCheckNNConstraint(wqueue
, tab
, partRel
, n
,
20245 true, false, true, ShareUpdateExclusiveLock
);
20250 * DropClonedTriggersFromPartition
20251 * subroutine for ATExecDetachPartition to remove any triggers that were
20252 * cloned to the partition when it was created-as-partition or attached.
20253 * This undoes what CloneRowTriggersToPartition did.
20256 DropClonedTriggersFromPartition(Oid partitionId
)
20262 ObjectAddresses
*objects
;
20264 objects
= new_object_addresses();
20267 * Scan pg_trigger to search for all triggers on this rel.
20269 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
20270 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
20271 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
20272 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
20273 true, NULL
, 1, &skey
);
20274 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
20276 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
20277 ObjectAddress trig
;
20279 /* Ignore triggers that weren't cloned */
20280 if (!OidIsValid(pg_trigger
->tgparentid
))
20284 * Ignore internal triggers that are implementation objects of foreign
20285 * keys, because these will be detached when the foreign keys
20288 if (OidIsValid(pg_trigger
->tgconstrrelid
))
20292 * This is ugly, but necessary: remove the dependency markings on the
20293 * trigger so that it can be removed.
20295 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20297 DEPENDENCY_PARTITION_PRI
);
20298 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20299 RelationRelationId
,
20300 DEPENDENCY_PARTITION_SEC
);
20302 /* remember this trigger to remove it below */
20303 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
20304 add_exact_object_address(&trig
, objects
);
20307 /* make the dependency removal visible to the deletion below */
20308 CommandCounterIncrement();
20309 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
20312 free_object_addresses(objects
);
20313 systable_endscan(scan
);
20314 table_close(tgrel
, RowExclusiveLock
);
20318 * Before acquiring lock on an index, acquire the same lock on the owning
20321 struct AttachIndexCallbackState
20325 bool lockedParentTbl
;
20329 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
20332 struct AttachIndexCallbackState
*state
;
20333 Form_pg_class classform
;
20336 state
= (struct AttachIndexCallbackState
*) arg
;
20338 if (!state
->lockedParentTbl
)
20340 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
20341 state
->lockedParentTbl
= true;
20345 * If we previously locked some other heap, and the name we're looking up
20346 * no longer refers to an index on that relation, release the now-useless
20347 * lock. XXX maybe we should do *after* we verify whether the index does
20348 * not actually belong to the same relation ...
20350 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
20352 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
20353 state
->partitionOid
= InvalidOid
;
20356 /* Didn't find a relation, so no need for locking or permission checks. */
20357 if (!OidIsValid(relOid
))
20360 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
20361 if (!HeapTupleIsValid(tuple
))
20362 return; /* concurrently dropped, so nothing to do */
20363 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
20364 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
20365 classform
->relkind
!= RELKIND_INDEX
)
20367 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20368 errmsg("\"%s\" is not an index", rv
->relname
)));
20369 ReleaseSysCache(tuple
);
20372 * Since we need only examine the heap's tupledesc, an access share lock
20373 * on it (preventing any DDL) is sufficient.
20375 state
->partitionOid
= IndexGetRelation(relOid
, false);
20376 LockRelationOid(state
->partitionOid
, AccessShareLock
);
20380 * ALTER INDEX i1 ATTACH PARTITION i2
20382 static ObjectAddress
20383 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
20387 Relation parentTbl
;
20388 ObjectAddress address
;
20391 struct AttachIndexCallbackState state
;
20394 * We need to obtain lock on the index 'name' to modify it, but we also
20395 * need to read its owning table's tuple descriptor -- so we need to lock
20396 * both. To avoid deadlocks, obtain lock on the table before doing so on
20397 * the index. Furthermore, we need to examine the parent table of the
20398 * partition, so lock that one too.
20400 state
.partitionOid
= InvalidOid
;
20401 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
20402 state
.lockedParentTbl
= false;
20404 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
20405 RangeVarCallbackForAttachIndex
,
20408 if (!OidIsValid(partIdxId
))
20410 (errcode(ERRCODE_UNDEFINED_OBJECT
),
20411 errmsg("index \"%s\" does not exist", name
->relname
)));
20413 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20414 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
20416 /* we already hold locks on both tables, so this is safe: */
20417 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
20418 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
20420 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
20422 /* Silently do nothing if already in the right state */
20423 currParent
= partIdx
->rd_rel
->relispartition
?
20424 get_partition_parent(partIdxId
, false) : InvalidOid
;
20425 if (currParent
!= RelationGetRelid(parentIdx
))
20427 IndexInfo
*childInfo
;
20428 IndexInfo
*parentInfo
;
20432 PartitionDesc partDesc
;
20434 cldConstrId
= InvalidOid
;
20437 * If this partition already has an index attached, refuse the
20440 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
20442 if (OidIsValid(currParent
))
20444 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20445 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20446 RelationGetRelationName(partIdx
),
20447 RelationGetRelationName(parentIdx
)),
20448 errdetail("Index \"%s\" is already attached to another index.",
20449 RelationGetRelationName(partIdx
))));
20451 /* Make sure it indexes a partition of the other index's table */
20452 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
20454 for (i
= 0; i
< partDesc
->nparts
; i
++)
20456 if (partDesc
->oids
[i
] == state
.partitionOid
)
20464 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20465 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20466 RelationGetRelationName(partIdx
),
20467 RelationGetRelationName(parentIdx
)),
20468 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20469 RelationGetRelationName(partIdx
),
20470 RelationGetRelationName(parentTbl
))));
20472 /* Ensure the indexes are compatible */
20473 childInfo
= BuildIndexInfo(partIdx
);
20474 parentInfo
= BuildIndexInfo(parentIdx
);
20475 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
20476 RelationGetDescr(parentTbl
),
20478 if (!CompareIndexInfo(childInfo
, parentInfo
,
20479 partIdx
->rd_indcollation
,
20480 parentIdx
->rd_indcollation
,
20481 partIdx
->rd_opfamily
,
20482 parentIdx
->rd_opfamily
,
20485 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20486 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20487 RelationGetRelationName(partIdx
),
20488 RelationGetRelationName(parentIdx
)),
20489 errdetail("The index definitions do not match.")));
20492 * If there is a constraint in the parent, make sure there is one in
20495 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
20496 RelationGetRelid(parentIdx
));
20498 if (OidIsValid(constraintOid
))
20500 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
20502 if (!OidIsValid(cldConstrId
))
20504 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20505 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20506 RelationGetRelationName(partIdx
),
20507 RelationGetRelationName(parentIdx
)),
20508 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20509 RelationGetRelationName(parentIdx
),
20510 RelationGetRelationName(parentTbl
),
20511 RelationGetRelationName(partIdx
))));
20515 * If it's a primary key, make sure the columns in the partition are
20518 if (parentIdx
->rd_index
->indisprimary
)
20519 verifyPartitionIndexNotNull(childInfo
, partTbl
);
20521 /* All good -- do it */
20522 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
20523 if (OidIsValid(constraintOid
))
20524 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
20525 RelationGetRelid(partTbl
));
20527 free_attrmap(attmap
);
20529 validatePartitionedIndex(parentIdx
, parentTbl
);
20532 relation_close(parentTbl
, AccessShareLock
);
20533 /* keep these locks till commit */
20534 relation_close(partTbl
, NoLock
);
20535 relation_close(partIdx
, NoLock
);
20541 * Verify whether the given partition already contains an index attached
20542 * to the given partitioned index. If so, raise an error.
20545 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
20549 existingIdx
= index_get_partition(partitionTbl
,
20550 RelationGetRelid(parentIdx
));
20551 if (OidIsValid(existingIdx
))
20553 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20554 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20555 RelationGetRelationName(partIdx
),
20556 RelationGetRelationName(parentIdx
)),
20557 errdetail("Another index is already attached for partition \"%s\".",
20558 RelationGetRelationName(partitionTbl
))));
20562 * Verify whether the set of attached partition indexes to a parent index on
20563 * a partitioned table is complete. If it is, mark the parent index valid.
20565 * This should be called each time a partition index is attached.
20568 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
20570 Relation inheritsRel
;
20575 bool updated
= false;
20577 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
20580 * Scan pg_inherits for this parent index. Count each valid index we find
20581 * (verifying the pg_index entry for each), and if we reach the total
20582 * amount we expect, we can mark this parent index as valid.
20584 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
20585 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
20586 BTEqualStrategyNumber
, F_OIDEQ
,
20587 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20588 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
20590 while ((inhTup
= systable_getnext(scan
)) != NULL
)
20592 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
20594 Form_pg_index indexForm
;
20596 indTup
= SearchSysCache1(INDEXRELID
,
20597 ObjectIdGetDatum(inhForm
->inhrelid
));
20598 if (!HeapTupleIsValid(indTup
))
20599 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
20600 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20601 if (indexForm
->indisvalid
)
20603 ReleaseSysCache(indTup
);
20606 /* Done with pg_inherits */
20607 systable_endscan(scan
);
20608 table_close(inheritsRel
, AccessShareLock
);
20611 * If we found as many inherited indexes as the partitioned table has
20612 * partitions, we're good; update pg_index to set indisvalid.
20614 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
20618 Form_pg_index indexForm
;
20620 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
20621 indTup
= SearchSysCacheCopy1(INDEXRELID
,
20622 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20623 if (!HeapTupleIsValid(indTup
))
20624 elog(ERROR
, "cache lookup failed for index %u",
20625 RelationGetRelid(partedIdx
));
20626 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20628 indexForm
->indisvalid
= true;
20631 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
20633 table_close(idxRel
, RowExclusiveLock
);
20634 heap_freetuple(indTup
);
20638 * If this index is in turn a partition of a larger index, validating it
20639 * might cause the parent to become valid also. Try that.
20641 if (updated
&& partedIdx
->rd_rel
->relispartition
)
20645 Relation parentIdx
,
20648 /* make sure we see the validation we just did */
20649 CommandCounterIncrement();
20651 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
20652 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
20653 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
20654 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
20655 Assert(!parentIdx
->rd_index
->indisvalid
);
20657 validatePartitionedIndex(parentIdx
, parentTbl
);
20659 relation_close(parentIdx
, AccessExclusiveLock
);
20660 relation_close(parentTbl
, AccessExclusiveLock
);
20665 * When attaching an index as a partition of a partitioned index which is a
20666 * primary key, verify that all the columns in the partition are marked NOT
20670 verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partition
)
20672 for (int i
= 0; i
< iinfo
->ii_NumIndexKeyAttrs
; i
++)
20674 Form_pg_attribute att
= TupleDescAttr(RelationGetDescr(partition
),
20675 iinfo
->ii_IndexAttrNumbers
[i
] - 1);
20677 if (!att
->attnotnull
)
20679 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
20680 errmsg("invalid primary key definition"),
20681 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20682 NameStr(att
->attname
),
20683 RelationGetRelationName(partition
)));
20688 * Return an OID list of constraints that reference the given relation
20689 * that are marked as having a parent constraints.
20692 GetParentedForeignKeyRefs(Relation partition
)
20694 Relation pg_constraint
;
20697 ScanKeyData key
[2];
20698 List
*constraints
= NIL
;
20701 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20704 if (RelationGetIndexList(partition
) == NIL
||
20705 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
20706 INDEX_ATTR_BITMAP_KEY
)))
20709 /* Search for constraints referencing this table */
20710 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
20711 ScanKeyInit(&key
[0],
20712 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
20713 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
20714 ScanKeyInit(&key
[1],
20715 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
20716 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
20718 /* XXX This is a seqscan, as we don't have a usable index */
20719 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
20720 while ((tuple
= systable_getnext(scan
)) != NULL
)
20722 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20725 * We only need to process constraints that are part of larger ones.
20727 if (!OidIsValid(constrForm
->conparentid
))
20730 constraints
= lappend_oid(constraints
, constrForm
->oid
);
20733 systable_endscan(scan
);
20734 table_close(pg_constraint
, AccessShareLock
);
20736 return constraints
;
20740 * During DETACH PARTITION, verify that any foreign keys pointing to the
20741 * partitioned table would not become invalid. An error is raised if any
20742 * referenced values exist.
20745 ATDetachCheckNoForeignKeyRefs(Relation partition
)
20750 constraints
= GetParentedForeignKeyRefs(partition
);
20752 foreach(cell
, constraints
)
20754 Oid constrOid
= lfirst_oid(cell
);
20756 Form_pg_constraint constrForm
;
20758 Trigger trig
= {0};
20760 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
20761 if (!HeapTupleIsValid(tuple
))
20762 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
20763 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20765 Assert(OidIsValid(constrForm
->conparentid
));
20766 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
20768 /* prevent data changes into the referencing table until commit */
20769 rel
= table_open(constrForm
->conrelid
, ShareLock
);
20771 trig
.tgoid
= InvalidOid
;
20772 trig
.tgname
= NameStr(constrForm
->conname
);
20773 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
20774 trig
.tgisinternal
= true;
20775 trig
.tgconstrrelid
= RelationGetRelid(partition
);
20776 trig
.tgconstrindid
= constrForm
->conindid
;
20777 trig
.tgconstraint
= constrForm
->oid
;
20778 trig
.tgdeferrable
= false;
20779 trig
.tginitdeferred
= false;
20780 /* we needn't fill in remaining fields */
20782 RI_PartitionRemove_Check(&trig
, rel
, partition
);
20784 ReleaseSysCache(tuple
);
20786 table_close(rel
, NoLock
);
20791 * resolve column compression specification to compression method.
20794 GetAttributeCompression(Oid atttypid
, const char *compression
)
20798 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
20799 return InvalidCompressionMethod
;
20802 * To specify a nondefault method, the column data type must be toastable.
20803 * Note this says nothing about whether the column's attstorage setting
20804 * permits compression; we intentionally allow attstorage and
20805 * attcompression to be independent. But with a non-toastable type,
20806 * attstorage could not be set to a value that would permit compression.
20808 * We don't actually need to enforce this, since nothing bad would happen
20809 * if attcompression were non-default; it would never be consulted. But
20810 * it seems more user-friendly to complain about a certainly-useless
20811 * attempt to set the property.
20813 if (!TypeIsToastable(atttypid
))
20815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20816 errmsg("column data type %s does not support compression",
20817 format_type_be(atttypid
))));
20819 cmethod
= CompressionNameToMethod(compression
);
20820 if (!CompressionMethodIsValid(cmethod
))
20822 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20823 errmsg("invalid compression method \"%s\"", compression
)));
20829 * resolve column storage specification
20832 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
20836 if (pg_strcasecmp(storagemode
, "plain") == 0)
20837 cstorage
= TYPSTORAGE_PLAIN
;
20838 else if (pg_strcasecmp(storagemode
, "external") == 0)
20839 cstorage
= TYPSTORAGE_EXTERNAL
;
20840 else if (pg_strcasecmp(storagemode
, "extended") == 0)
20841 cstorage
= TYPSTORAGE_EXTENDED
;
20842 else if (pg_strcasecmp(storagemode
, "main") == 0)
20843 cstorage
= TYPSTORAGE_MAIN
;
20844 else if (pg_strcasecmp(storagemode
, "default") == 0)
20845 cstorage
= get_typstorage(atttypid
);
20848 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20849 errmsg("invalid storage type \"%s\"",
20853 * safety check: do not allow toasted storage modes unless column datatype
20856 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
20858 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20859 errmsg("column data type %s can only have storage PLAIN",
20860 format_type_be(atttypid
))));