1 /*-------------------------------------------------------------------------
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
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 "executor/executor.h"
70 #include "foreign/fdwapi.h"
71 #include "foreign/foreign.h"
72 #include "miscadmin.h"
73 #include "nodes/makefuncs.h"
74 #include "nodes/nodeFuncs.h"
75 #include "nodes/parsenodes.h"
76 #include "optimizer/optimizer.h"
77 #include "parser/parse_coerce.h"
78 #include "parser/parse_collate.h"
79 #include "parser/parse_expr.h"
80 #include "parser/parse_relation.h"
81 #include "parser/parse_type.h"
82 #include "parser/parse_utilcmd.h"
83 #include "parser/parser.h"
84 #include "partitioning/partbounds.h"
85 #include "partitioning/partdesc.h"
87 #include "rewrite/rewriteDefine.h"
88 #include "rewrite/rewriteHandler.h"
89 #include "rewrite/rewriteManip.h"
90 #include "storage/bufmgr.h"
91 #include "storage/lmgr.h"
92 #include "storage/lock.h"
93 #include "storage/predicate.h"
94 #include "storage/smgr.h"
95 #include "tcop/utility.h"
96 #include "utils/acl.h"
97 #include "utils/builtins.h"
98 #include "utils/fmgroids.h"
99 #include "utils/inval.h"
100 #include "utils/lsyscache.h"
101 #include "utils/memutils.h"
102 #include "utils/partcache.h"
103 #include "utils/relcache.h"
104 #include "utils/ruleutils.h"
105 #include "utils/snapmgr.h"
106 #include "utils/syscache.h"
107 #include "utils/timestamp.h"
108 #include "utils/typcache.h"
109 #include "utils/usercontext.h"
112 * ON COMMIT action list
114 typedef struct OnCommitItem
116 Oid relid
; /* relid of relation */
117 OnCommitAction oncommit
; /* what to do at end of xact */
120 * If this entry was created during the current transaction,
121 * creating_subid is the ID of the creating subxact; if created in a prior
122 * transaction, creating_subid is zero. If deleted during the current
123 * transaction, deleting_subid is the ID of the deleting subxact; if no
124 * deletion request is pending, deleting_subid is zero.
126 SubTransactionId creating_subid
;
127 SubTransactionId deleting_subid
;
130 static List
*on_commits
= NIL
;
134 * State information for ALTER TABLE
136 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
137 * structs, one for each table modified by the operation (the named table
138 * plus any child tables that are affected). We save lists of subcommands
139 * to apply to this table (possibly modified by parse transformation steps);
140 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
141 * necessary information is stored in the constraints and newvals lists.
143 * Phase 2 is divided into multiple passes; subcommands are executed in
144 * a pass determined by subcommand type.
147 typedef enum AlterTablePass
149 AT_PASS_UNSET
= -1, /* UNSET will cause ERROR */
150 AT_PASS_DROP
, /* DROP (all flavors) */
151 AT_PASS_ALTER_TYPE
, /* ALTER COLUMN TYPE */
152 AT_PASS_ADD_COL
, /* ADD COLUMN */
153 AT_PASS_SET_EXPRESSION
, /* ALTER SET EXPRESSION */
154 AT_PASS_OLD_COL_ATTRS
, /* re-install attnotnull */
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
338 * ForeignTruncateInfo
340 * Information related to truncation of foreign tables. This is used for
341 * the elements in a hash table. It uses the server OID as lookup key,
342 * and includes a per-server list of all foreign tables involved in the
345 typedef struct ForeignTruncateInfo
349 } ForeignTruncateInfo
;
352 * Partition tables are expected to be dropped when the parent partitioned
353 * table gets dropped. Hence for partitioning we use AUTO dependency.
354 * Otherwise, for regular inheritance use NORMAL dependency.
356 #define child_dependency_type(child_is_partition) \
357 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
359 static void truncate_check_rel(Oid relid
, Form_pg_class reltuple
);
360 static void truncate_check_perms(Oid relid
, Form_pg_class reltuple
);
361 static void truncate_check_activity(Relation rel
);
362 static void RangeVarCallbackForTruncate(const RangeVar
*relation
,
363 Oid relId
, Oid oldRelId
, void *arg
);
364 static List
*MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
365 bool is_partition
, List
**supconstr
,
367 static List
*MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
);
368 static void MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
);
369 static ColumnDef
*MergeInheritedAttribute(List
*inh_columns
, int exist_attno
, const ColumnDef
*newdef
);
370 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
);
371 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
372 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
373 bool child_is_partition
);
374 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
375 int32 seqNumber
, Relation inhRelation
,
376 bool child_is_partition
);
377 static int findAttrByName(const char *attributeName
, const List
*columns
);
378 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
379 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
380 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
381 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
383 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
384 bool recurse
, bool recursing
, LOCKMODE lockmode
);
385 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
386 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
388 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
389 Relation rel
, char *constrName
,
390 bool recurse
, bool recursing
, LOCKMODE lockmode
);
391 static int transformColumnNameList(Oid relId
, List
*colList
,
392 int16
*attnums
, Oid
*atttypids
);
393 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
395 int16
*attnums
, Oid
*atttypids
,
396 Oid
*opclasses
, bool *pk_has_without_overlaps
);
397 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
398 int numattrs
, int16
*attnums
,
399 bool with_period
, Oid
*opclasses
,
400 bool *pk_has_without_overlaps
);
401 static void checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
);
402 static CoercionPathType
findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
,
404 static void validateForeignKeyConstraint(char *conname
,
405 Relation rel
, Relation pkrel
,
406 Oid pkindOid
, Oid constraintOid
, bool hasperiod
);
407 static void ATController(AlterTableStmt
*parsetree
,
408 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
409 AlterTableUtilityContext
*context
);
410 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
411 bool recurse
, bool recursing
, LOCKMODE lockmode
,
412 AlterTableUtilityContext
*context
);
413 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
414 AlterTableUtilityContext
*context
);
415 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
416 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
417 AlterTableUtilityContext
*context
);
418 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
419 Relation rel
, AlterTableCmd
*cmd
,
420 bool recurse
, LOCKMODE lockmode
,
421 AlterTablePass cur_pass
,
422 AlterTableUtilityContext
*context
);
423 static void ATRewriteTables(AlterTableStmt
*parsetree
,
424 List
**wqueue
, LOCKMODE lockmode
,
425 AlterTableUtilityContext
*context
);
426 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
);
427 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
428 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
429 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
430 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
431 AlterTableUtilityContext
*context
);
432 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
433 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
435 AlterTableUtilityContext
*context
);
436 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
437 DropBehavior behavior
);
438 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
439 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
440 AlterTableUtilityContext
*context
);
441 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
442 Relation rel
, AlterTableCmd
**cmd
,
443 bool recurse
, bool recursing
,
444 LOCKMODE lockmode
, AlterTablePass cur_pass
,
445 AlterTableUtilityContext
*context
);
446 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
448 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
449 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
450 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
452 static bool set_attnotnull(List
**wqueue
, Relation rel
,
453 AttrNumber attnum
, bool recurse
, LOCKMODE lockmode
);
454 static ObjectAddress
ATExecSetNotNull(List
**wqueue
, Relation rel
,
455 char *constrname
, char *colName
,
456 bool recurse
, bool recursing
,
457 List
**readyRels
, LOCKMODE lockmode
);
458 static ObjectAddress
ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
459 const char *colName
, LOCKMODE lockmode
);
460 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
461 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
462 List
*testConstraint
, List
*provenConstraint
);
463 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
464 Node
*newDefault
, LOCKMODE lockmode
);
465 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
467 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
468 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
469 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
470 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
471 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
472 bool recurse
, bool recursing
);
473 static ObjectAddress
ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
474 Node
*newExpr
, LOCKMODE lockmode
);
475 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
476 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
477 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
478 Node
*newValue
, LOCKMODE lockmode
);
479 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
480 Node
*options
, bool isReset
, LOCKMODE lockmode
);
481 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
482 Node
*newValue
, LOCKMODE lockmode
);
483 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
484 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
485 AlterTableUtilityContext
*context
);
486 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
487 DropBehavior behavior
,
488 bool recurse
, bool recursing
,
489 bool missing_ok
, LOCKMODE lockmode
,
490 ObjectAddresses
*addrs
);
491 static void ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
492 LOCKMODE lockmode
, AlterTableUtilityContext
*context
);
493 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
494 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
495 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
496 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
497 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
498 AlteredTableInfo
*tab
, Relation rel
,
499 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
501 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
502 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
503 IndexStmt
*stmt
, LOCKMODE lockmode
);
504 static ObjectAddress
ATAddCheckNNConstraint(List
**wqueue
,
505 AlteredTableInfo
*tab
, Relation rel
,
507 bool recurse
, bool recursing
, bool is_readd
,
509 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
510 Relation rel
, Constraint
*fkconstraint
,
511 bool recurse
, bool recursing
,
513 static ObjectAddress
addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
,
514 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
515 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
516 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
517 int numfkdelsetcols
, int16
*fkdelsetcols
,
519 Oid parentDelTrigger
, Oid parentUpdTrigger
,
521 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
522 int numfksetcols
, const int16
*fksetcolsattnums
,
524 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
525 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
526 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
527 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
528 int numfkdelsetcols
, int16
*fkdelsetcols
,
529 bool old_check_ok
, LOCKMODE lockmode
,
530 Oid parentInsTrigger
, Oid parentUpdTrigger
,
533 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
534 Relation partitionRel
);
535 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
536 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
538 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
539 Constraint
*fkconstraint
, Oid constraintOid
,
541 Oid parentInsTrigger
, Oid parentUpdTrigger
,
542 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
543 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
544 Constraint
*fkconstraint
, Oid constraintOid
,
546 Oid parentDelTrigger
, Oid parentUpdTrigger
,
547 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
548 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
550 Oid parentConstrOid
, int numfks
,
551 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
553 Oid parentInsTrigger
,
554 Oid parentUpdTrigger
,
556 static void GetForeignKeyActionTriggers(Relation trigrel
,
557 Oid conoid
, Oid confrelid
, Oid conrelid
,
558 Oid
*deleteTriggerOid
,
559 Oid
*updateTriggerOid
);
560 static void GetForeignKeyCheckTriggers(Relation trigrel
,
561 Oid conoid
, Oid confrelid
, Oid conrelid
,
562 Oid
*insertTriggerOid
,
563 Oid
*updateTriggerOid
);
564 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
565 DropBehavior behavior
, bool recurse
,
566 bool missing_ok
, LOCKMODE lockmode
);
567 static ObjectAddress
dropconstraint_internal(Relation rel
,
568 HeapTuple constraintTup
, DropBehavior behavior
,
569 bool recurse
, bool recursing
,
570 bool missing_ok
, List
**readyRels
,
572 static void ATPrepAlterColumnType(List
**wqueue
,
573 AlteredTableInfo
*tab
, Relation rel
,
574 bool recurse
, bool recursing
,
575 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
576 AlterTableUtilityContext
*context
);
577 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
578 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
579 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
580 static void RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
581 Relation rel
, AttrNumber attnum
, const char *colName
);
582 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
583 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
584 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
585 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
587 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
588 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
590 static void RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
,
591 Oid objid
, Relation rel
, List
*domname
,
592 const char *conname
);
593 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
594 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
595 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
596 List
*options
, LOCKMODE lockmode
);
597 static void change_owner_fix_column_acls(Oid relationOid
,
598 Oid oldOwnerId
, Oid newOwnerId
);
599 static void change_owner_recurse_to_sequences(Oid relationOid
,
600 Oid newOwnerId
, LOCKMODE lockmode
);
601 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
603 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
604 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
605 static void ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethod
);
606 static bool ATPrepChangePersistence(Relation rel
, bool toLogged
);
607 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
608 const char *tablespacename
, LOCKMODE lockmode
);
609 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
610 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
611 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
612 AlterTableType operation
,
614 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
615 char fires_when
, bool skip_system
, bool recurse
,
617 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
618 char fires_when
, LOCKMODE lockmode
);
619 static void ATPrepAddInherit(Relation child_rel
);
620 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
621 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
622 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
623 DependencyType deptype
);
624 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
625 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
626 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
627 static void ATExecGenericOptions(Relation rel
, List
*options
);
628 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
629 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
630 static ObjectAddress
ATExecSetCompression(Relation rel
,
631 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
633 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
634 static const char *storage_name(char c
);
636 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
637 Oid oldRelOid
, void *arg
);
638 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
639 Oid oldrelid
, void *arg
);
640 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
641 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
642 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
643 PartitionStrategy strategy
);
644 static void CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
);
645 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
646 bool expect_detached
);
647 static void ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
,
649 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
651 AlterTableUtilityContext
*context
);
652 static void AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
);
653 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
654 List
*partConstraint
,
655 bool validate_default
);
656 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
657 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
658 static void DropClonedTriggersFromPartition(Oid partitionId
);
659 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
660 Relation rel
, RangeVar
*name
,
662 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
663 bool concurrent
, Oid defaultPartOid
);
664 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
665 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
667 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
668 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
669 Relation partitionTbl
);
670 static void verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partIdx
);
671 static List
*GetParentedForeignKeyRefs(Relation partition
);
672 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
673 static char GetAttributeCompression(Oid atttypid
, const char *compression
);
674 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
676 static void ATExecSplitPartition(List
**wqueue
, AlteredTableInfo
*tab
,
677 Relation rel
, PartitionCmd
*cmd
,
678 AlterTableUtilityContext
*context
);
679 static void ATExecMergePartitions(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
680 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
);
682 /* ----------------------------------------------------------------
684 * Creates a new relation.
686 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
687 * The other arguments are used to extend the behavior for other cases:
688 * relkind: relkind to assign to the new relation
689 * ownerId: if not InvalidOid, use this as the new relation's owner.
690 * typaddress: if not null, it's set to the pg_type entry's address.
691 * queryString: for error reporting
693 * Note that permissions checks are done against current user regardless of
694 * ownerId. A nonzero ownerId is used when someone is creating a relation
695 * "on behalf of" someone else, so we still want to see that the current user
696 * has permissions to do it.
698 * If successful, returns the address of the new relation.
699 * ----------------------------------------------------------------
702 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
703 ObjectAddress
*typaddress
, const char *queryString
)
705 char relname
[NAMEDATALEN
];
710 TupleDesc descriptor
;
712 List
*old_constraints
;
715 List
*cookedDefaults
;
721 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
723 ObjectAddress address
;
724 LOCKMODE parentLockmode
;
725 Oid accessMethodId
= InvalidOid
;
728 * Truncate relname to appropriate length (probably a waste of time, as
729 * parser should have done this already).
731 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
734 * Check consistency of arguments
736 if (stmt
->oncommit
!= ONCOMMIT_NOOP
737 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
739 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
740 errmsg("ON COMMIT can only be used on temporary tables")));
742 if (stmt
->partspec
!= NULL
)
744 if (relkind
!= RELKIND_RELATION
)
745 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
747 relkind
= RELKIND_PARTITIONED_TABLE
;
754 * Look up the namespace in which we are supposed to create the relation,
755 * check we have permission to create there, lock it against concurrent
756 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
757 * namespace is selected.
760 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
763 * Security check: disallow creating temp tables from security-restricted
764 * code. This is needed because calling code might not expect untrusted
765 * tables to appear in pg_temp at the front of its search path.
767 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
768 && InSecurityRestrictedOperation())
770 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
771 errmsg("cannot create temporary table within security-restricted operation")));
774 * Determine the lockmode to use when scanning parents. A self-exclusive
775 * lock is needed here.
777 * For regular inheritance, if two backends attempt to add children to the
778 * same parent simultaneously, and that parent has no pre-existing
779 * children, then both will attempt to update the parent's relhassubclass
780 * field, leading to a "tuple concurrently updated" error. Also, this
781 * interlocks against a concurrent ANALYZE on the parent table, which
782 * might otherwise be attempting to clear the parent's relhassubclass
783 * field, if its previous children were recently dropped.
785 * If the child table is a partition, then we instead grab an exclusive
786 * lock on the parent because its partition descriptor will be changed by
787 * addition of the new partition.
789 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
790 ShareUpdateExclusiveLock
);
792 /* Determine the list of OIDs of the parents. */
794 foreach(listptr
, stmt
->inhRelations
)
796 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
799 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
802 * Reject duplications in the list of parents.
804 if (list_member_oid(inheritOids
, parentOid
))
806 (errcode(ERRCODE_DUPLICATE_TABLE
),
807 errmsg("relation \"%s\" would be inherited from more than once",
808 get_rel_name(parentOid
))));
810 inheritOids
= lappend_oid(inheritOids
, parentOid
);
814 * Select tablespace to use: an explicitly indicated one, or (in the case
815 * of a partitioned table) the parent's, if it has one.
817 if (stmt
->tablespacename
)
819 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
821 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
823 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
824 errmsg("cannot specify default tablespace for partitioned relations")));
826 else if (stmt
->partbound
)
828 Assert(list_length(inheritOids
) == 1);
829 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
832 tablespaceId
= InvalidOid
;
834 /* still nothing? use the default */
835 if (!OidIsValid(tablespaceId
))
836 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
839 /* Check permissions except when using database's default */
840 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
844 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
846 if (aclresult
!= ACLCHECK_OK
)
847 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
848 get_tablespace_name(tablespaceId
));
851 /* In all cases disallow placing user relations in pg_global */
852 if (tablespaceId
== GLOBALTABLESPACE_OID
)
854 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
855 errmsg("only shared relations can be placed in pg_global tablespace")));
857 /* Identify user ID that will own the table */
858 if (!OidIsValid(ownerId
))
859 ownerId
= GetUserId();
862 * Parse and validate reloptions, if any.
864 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
870 (void) view_reloptions(reloptions
, true);
872 case RELKIND_PARTITIONED_TABLE
:
873 (void) partitioned_table_reloptions(reloptions
, true);
876 (void) heap_reloptions(relkind
, reloptions
, true);
879 if (stmt
->ofTypename
)
883 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
885 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
886 if (aclresult
!= ACLCHECK_OK
)
887 aclcheck_error_type(aclresult
, ofTypeId
);
890 ofTypeId
= InvalidOid
;
893 * Look up inheritance ancestors and generate relation schema, including
894 * inherited attributes. (Note that stmt->tableElts is destructively
895 * modified by MergeAttributes.)
898 MergeAttributes(stmt
->tableElts
, inheritOids
,
899 stmt
->relation
->relpersistence
,
900 stmt
->partbound
!= NULL
,
901 &old_constraints
, &old_notnulls
);
904 * Create a tuple descriptor from the relation schema. Note that this
905 * deals with column names, types, and in-descriptor NOT NULL flags, but
906 * not default values, NOT NULL or CHECK constraints; we handle those
909 descriptor
= BuildDescForRelation(stmt
->tableElts
);
912 * Find columns with default values and prepare for insertion of the
913 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
914 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
915 * while raw defaults go into a list of RawColumnDefault structs that will
916 * be processed by AddRelationNewConstraints. (We can't deal with raw
917 * expressions until we can do transformExpr.)
919 * We can set the atthasdef flags now in the tuple descriptor; this just
920 * saves StoreAttrDefault from having to do an immediate update of the
924 cookedDefaults
= NIL
;
927 foreach(listptr
, stmt
->tableElts
)
929 ColumnDef
*colDef
= lfirst(listptr
);
930 Form_pg_attribute attr
;
933 attr
= TupleDescAttr(descriptor
, attnum
- 1);
935 if (colDef
->raw_default
!= NULL
)
937 RawColumnDefault
*rawEnt
;
939 Assert(colDef
->cooked_default
== NULL
);
941 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
942 rawEnt
->attnum
= attnum
;
943 rawEnt
->raw_default
= colDef
->raw_default
;
944 rawEnt
->missingMode
= false;
945 rawEnt
->generated
= colDef
->generated
;
946 rawDefaults
= lappend(rawDefaults
, rawEnt
);
947 attr
->atthasdef
= true;
949 else if (colDef
->cooked_default
!= NULL
)
951 CookedConstraint
*cooked
;
953 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
954 cooked
->contype
= CONSTR_DEFAULT
;
955 cooked
->conoid
= InvalidOid
; /* until created */
957 cooked
->attnum
= attnum
;
958 cooked
->expr
= colDef
->cooked_default
;
959 cooked
->skip_validation
= false;
960 cooked
->is_local
= true; /* not used for defaults */
961 cooked
->inhcount
= 0; /* ditto */
962 cooked
->is_no_inherit
= false;
963 cookedDefaults
= lappend(cookedDefaults
, cooked
);
964 attr
->atthasdef
= true;
969 * For relations with table AM and partitioned tables, select access
970 * method to use: an explicitly indicated one, or (in the case of a
971 * partitioned table) the parent's, if it has one.
973 if (stmt
->accessMethod
!= NULL
)
975 Assert(RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
);
976 accessMethodId
= get_table_am_oid(stmt
->accessMethod
, false);
978 else if (RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
)
982 Assert(list_length(inheritOids
) == 1);
983 accessMethodId
= get_rel_relam(linitial_oid(inheritOids
));
986 if (RELKIND_HAS_TABLE_AM(relkind
) && !OidIsValid(accessMethodId
))
987 accessMethodId
= get_table_am_oid(default_table_access_method
, false);
991 * Create the relation. Inherited defaults and constraints are passed in
992 * for immediate handling --- since they don't need parsing, they can be
993 * stored immediately.
995 relationId
= heap_create_with_catalog(relname
,
1004 list_concat(cookedDefaults
,
1007 stmt
->relation
->relpersistence
,
1013 allowSystemTableMods
,
1019 * We must bump the command counter to make the newly-created relation
1020 * tuple visible for opening.
1022 CommandCounterIncrement();
1025 * Open the new relation and acquire exclusive lock on it. This isn't
1026 * really necessary for locking out other backends (since they can't see
1027 * the new rel anyway until we commit), but it keeps the lock manager from
1028 * complaining about deadlock risks.
1030 rel
= relation_open(relationId
, AccessExclusiveLock
);
1033 * Now add any newly specified column default and generation expressions
1034 * to the new relation. These are passed to us in the form of raw
1035 * parsetrees; we need to transform them to executable expression trees
1036 * before they can be added. The most convenient way to do that is to
1037 * apply the parser's transformExpr routine, but transformExpr doesn't
1038 * work unless we have a pre-existing relation. So, the transformation has
1039 * to be postponed to this final step of CREATE TABLE.
1041 * This needs to be before processing the partitioning clauses because
1042 * those could refer to generated columns.
1045 AddRelationNewConstraints(rel
, rawDefaults
, NIL
,
1046 true, true, false, queryString
);
1049 * Make column generation expressions visible for use by partitioning.
1051 CommandCounterIncrement();
1053 /* Process and store partition bound, if any. */
1054 if (stmt
->partbound
)
1056 PartitionBoundSpec
*bound
;
1058 Oid parentId
= linitial_oid(inheritOids
),
1062 ParseNamespaceItem
*nsitem
;
1064 /* Already have strong enough lock on the parent */
1065 parent
= table_open(parentId
, NoLock
);
1068 * We are going to try to validate the partition bound specification
1069 * against the partition key of parentRel, so it better have one.
1071 if (parent
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
1073 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1074 errmsg("\"%s\" is not partitioned",
1075 RelationGetRelationName(parent
))));
1078 * The partition constraint of the default partition depends on the
1079 * partition bounds of every other partition. It is possible that
1080 * another backend might be about to execute a query on the default
1081 * partition table, and that the query relies on previously cached
1082 * default partition constraints. We must therefore take a table lock
1083 * strong enough to prevent all queries on the default partition from
1084 * proceeding until we commit and send out a shared-cache-inval notice
1085 * that will make them update their index lists.
1087 * Order of locking: The relation being added won't be visible to
1088 * other backends until it is committed, hence here in
1089 * DefineRelation() the order of locking the default partition and the
1090 * relation being added does not matter. But at all other places we
1091 * need to lock the default relation before we lock the relation being
1092 * added or removed i.e. we should take the lock in same order at all
1093 * the places such that lock parent, lock default partition and then
1094 * lock the partition so as to avoid a deadlock.
1097 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent
,
1099 if (OidIsValid(defaultPartOid
))
1100 defaultRel
= table_open(defaultPartOid
, AccessExclusiveLock
);
1102 /* Transform the bound values */
1103 pstate
= make_parsestate(NULL
);
1104 pstate
->p_sourcetext
= queryString
;
1107 * Add an nsitem containing this relation, so that transformExpr
1108 * called on partition bound expressions is able to report errors
1109 * using a proper context.
1111 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
1112 NULL
, false, false);
1113 addNSItemToQuery(pstate
, nsitem
, false, true, true);
1115 bound
= transformPartitionBound(pstate
, parent
, stmt
->partbound
);
1118 * Check first that the new partition's bound is valid and does not
1119 * overlap with any of existing partitions of the parent.
1121 check_new_partition_bound(relname
, parent
, bound
, pstate
);
1124 * If the default partition exists, its partition constraints will
1125 * change after the addition of this new partition such that it won't
1126 * allow any row that qualifies for this new partition. So, check that
1127 * the existing data in the default partition satisfies the constraint
1128 * as it will exist after adding this partition.
1130 if (OidIsValid(defaultPartOid
))
1132 check_default_partition_contents(parent
, defaultRel
, bound
);
1133 /* Keep the lock until commit. */
1134 table_close(defaultRel
, NoLock
);
1137 /* Update the pg_class entry. */
1138 StorePartitionBound(rel
, parent
, bound
);
1140 table_close(parent
, NoLock
);
1143 /* Store inheritance information for new rel. */
1144 StoreCatalogInheritance(relationId
, inheritOids
, stmt
->partbound
!= NULL
);
1147 * Process the partitioning specification (if any) and store the partition
1148 * key information into the catalog.
1154 AttrNumber partattrs
[PARTITION_MAX_KEYS
];
1155 Oid partopclass
[PARTITION_MAX_KEYS
];
1156 Oid partcollation
[PARTITION_MAX_KEYS
];
1157 List
*partexprs
= NIL
;
1159 pstate
= make_parsestate(NULL
);
1160 pstate
->p_sourcetext
= queryString
;
1162 partnatts
= list_length(stmt
->partspec
->partParams
);
1164 /* Protect fixed-size arrays here and in executor */
1165 if (partnatts
> PARTITION_MAX_KEYS
)
1167 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
1168 errmsg("cannot partition using more than %d columns",
1169 PARTITION_MAX_KEYS
)));
1172 * We need to transform the raw parsetrees corresponding to partition
1173 * expressions into executable expression trees. Like column defaults
1174 * and CHECK constraints, we could not have done the transformation
1177 stmt
->partspec
= transformPartitionSpec(rel
, stmt
->partspec
);
1179 ComputePartitionAttrs(pstate
, rel
, stmt
->partspec
->partParams
,
1180 partattrs
, &partexprs
, partopclass
,
1181 partcollation
, stmt
->partspec
->strategy
);
1183 StorePartitionKey(rel
, stmt
->partspec
->strategy
, partnatts
, partattrs
,
1185 partopclass
, partcollation
);
1187 /* make it all visible */
1188 CommandCounterIncrement();
1192 * If we're creating a partition, create now all the indexes, triggers,
1193 * FKs defined in the parent.
1195 * We can't do it earlier, because DefineIndex wants to know the partition
1196 * key which we just stored.
1198 if (stmt
->partbound
)
1200 Oid parentId
= linitial_oid(inheritOids
);
1205 /* Already have strong enough lock on the parent */
1206 parent
= table_open(parentId
, NoLock
);
1207 idxlist
= RelationGetIndexList(parent
);
1210 * For each index in the parent table, create one in the partition
1212 foreach(cell
, idxlist
)
1214 Relation idxRel
= index_open(lfirst_oid(cell
), AccessShareLock
);
1219 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1221 if (idxRel
->rd_index
->indisunique
)
1223 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1224 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1225 RelationGetRelationName(parent
)),
1226 errdetail("Table \"%s\" contains indexes that are unique.",
1227 RelationGetRelationName(parent
))));
1230 index_close(idxRel
, AccessShareLock
);
1235 attmap
= build_attrmap_by_name(RelationGetDescr(rel
),
1236 RelationGetDescr(parent
),
1239 generateClonedIndexStmt(NULL
, idxRel
,
1240 attmap
, &constraintOid
);
1241 DefineIndex(RelationGetRelid(rel
),
1244 RelationGetRelid(idxRel
),
1247 false, false, false, false, false);
1249 index_close(idxRel
, AccessShareLock
);
1255 * If there are any row-level triggers, clone them to the new
1258 if (parent
->trigdesc
!= NULL
)
1259 CloneRowTriggersToPartition(parent
, rel
);
1262 * And foreign keys too. Note that because we're freshly creating the
1263 * table, there is no need to verify these new constraints.
1265 CloneForeignKeyConstraints(NULL
, parent
, rel
);
1267 table_close(parent
, NoLock
);
1271 * Now add any newly specified CHECK constraints to the new relation. Same
1272 * as for defaults above, but these need to come after partitioning is set
1275 if (stmt
->constraints
)
1276 AddRelationNewConstraints(rel
, NIL
, stmt
->constraints
,
1277 true, true, false, queryString
);
1280 * Finally, merge the not-null constraints that are declared directly with
1281 * those that come from parent relations (making sure to count inheritance
1282 * appropriately for each), create them, and set the attnotnull flag on
1283 * columns that don't yet have it.
1285 nncols
= AddRelationNotNullConstraints(rel
, stmt
->nnconstraints
,
1287 foreach(listptr
, nncols
)
1288 set_attnotnull(NULL
, rel
, lfirst_int(listptr
), false, NoLock
);
1290 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1293 * Clean up. We keep lock on new relation (although it shouldn't be
1294 * visible to anyone else anyway, until commit).
1296 relation_close(rel
, NoLock
);
1302 * BuildDescForRelation
1304 * Given a list of ColumnDef nodes, build a TupleDesc.
1306 * Note: tdtypeid will need to be filled in later on.
1309 BuildDescForRelation(const List
*columns
)
1323 * allocate a new tuple descriptor
1325 natts
= list_length(columns
);
1326 desc
= CreateTemplateTupleDesc(natts
);
1327 has_not_null
= false;
1333 ColumnDef
*entry
= lfirst(l
);
1334 AclResult aclresult
;
1335 Form_pg_attribute att
;
1338 * for each entry in the list, get the name and type information from
1339 * the list and have TupleDescInitEntry fill in the attribute
1340 * information we need.
1344 attname
= entry
->colname
;
1345 typenameTypeIdAndMod(NULL
, entry
->typeName
, &atttypid
, &atttypmod
);
1347 aclresult
= object_aclcheck(TypeRelationId
, atttypid
, GetUserId(), ACL_USAGE
);
1348 if (aclresult
!= ACLCHECK_OK
)
1349 aclcheck_error_type(aclresult
, atttypid
);
1351 attcollation
= GetColumnDefCollation(NULL
, entry
, atttypid
);
1352 attdim
= list_length(entry
->typeName
->arrayBounds
);
1353 if (attdim
> PG_INT16_MAX
)
1355 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1356 errmsg("too many array dimensions"));
1358 if (entry
->typeName
->setof
)
1360 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
1361 errmsg("column \"%s\" cannot be declared SETOF",
1364 TupleDescInitEntry(desc
, attnum
, attname
,
1365 atttypid
, atttypmod
, attdim
);
1366 att
= TupleDescAttr(desc
, attnum
- 1);
1368 /* Override TupleDescInitEntry's settings as requested */
1369 TupleDescInitEntryCollation(desc
, attnum
, attcollation
);
1371 /* Fill in additional stuff not handled by TupleDescInitEntry */
1372 att
->attnotnull
= entry
->is_not_null
;
1373 has_not_null
|= entry
->is_not_null
;
1374 att
->attislocal
= entry
->is_local
;
1375 att
->attinhcount
= entry
->inhcount
;
1376 att
->attidentity
= entry
->identity
;
1377 att
->attgenerated
= entry
->generated
;
1378 att
->attcompression
= GetAttributeCompression(att
->atttypid
, entry
->compression
);
1380 att
->attstorage
= entry
->storage
;
1381 else if (entry
->storage_name
)
1382 att
->attstorage
= GetAttributeStorage(att
->atttypid
, entry
->storage_name
);
1387 TupleConstr
*constr
= (TupleConstr
*) palloc0(sizeof(TupleConstr
));
1389 constr
->has_not_null
= true;
1390 constr
->has_generated_stored
= false;
1391 constr
->defval
= NULL
;
1392 constr
->missing
= NULL
;
1393 constr
->num_defval
= 0;
1394 constr
->check
= NULL
;
1395 constr
->num_check
= 0;
1396 desc
->constr
= constr
;
1400 desc
->constr
= NULL
;
1407 * Emit the right error or warning message for a "DROP" command issued on a
1408 * non-existent relation
1411 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1413 const struct dropmsgstrings
*rentry
;
1415 if (rel
->schemaname
!= NULL
&&
1416 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1421 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1422 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1427 (errmsg("schema \"%s\" does not exist, skipping",
1433 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1435 if (rentry
->kind
== rightkind
)
1440 (errcode(rentry
->nonexistent_code
),
1441 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1445 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1451 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1455 * Emit the right error message for a "DROP" command issued on a
1456 * relation of the wrong type
1459 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1461 const struct dropmsgstrings
*rentry
;
1462 const struct dropmsgstrings
*wentry
;
1464 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1465 if (rentry
->kind
== rightkind
)
1467 Assert(rentry
->kind
!= '\0');
1469 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1470 if (wentry
->kind
== wrongkind
)
1472 /* wrongkind could be something we don't have in our table... */
1475 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1476 errmsg(rentry
->nota_msg
, relname
),
1477 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1482 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1483 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1486 RemoveRelations(DropStmt
*drop
)
1488 ObjectAddresses
*objects
;
1492 LOCKMODE lockmode
= AccessExclusiveLock
;
1494 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1495 if (drop
->concurrent
)
1498 * Note that for temporary relations this lock may get upgraded later
1499 * on, but as no other session can access a temporary relation, this
1502 lockmode
= ShareUpdateExclusiveLock
;
1503 Assert(drop
->removeType
== OBJECT_INDEX
);
1504 if (list_length(drop
->objects
) != 1)
1506 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1507 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1508 if (drop
->behavior
== DROP_CASCADE
)
1510 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1511 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1515 * First we identify all the relations, then we delete them in a single
1516 * performMultipleDeletions() call. This is to avoid unwanted DROP
1517 * RESTRICT errors if one of the relations depends on another.
1520 /* Determine required relkind */
1521 switch (drop
->removeType
)
1524 relkind
= RELKIND_RELATION
;
1528 relkind
= RELKIND_INDEX
;
1531 case OBJECT_SEQUENCE
:
1532 relkind
= RELKIND_SEQUENCE
;
1536 relkind
= RELKIND_VIEW
;
1539 case OBJECT_MATVIEW
:
1540 relkind
= RELKIND_MATVIEW
;
1543 case OBJECT_FOREIGN_TABLE
:
1544 relkind
= RELKIND_FOREIGN_TABLE
;
1548 elog(ERROR
, "unrecognized drop object type: %d",
1549 (int) drop
->removeType
);
1550 relkind
= 0; /* keep compiler quiet */
1554 /* Lock and validate each relation; build a list of object addresses */
1555 objects
= new_object_addresses();
1557 foreach(cell
, drop
->objects
)
1559 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1562 struct DropRelationCallbackState state
;
1565 * These next few steps are a great deal like relation_openrv, but we
1566 * don't bother building a relcache entry since we don't need it.
1568 * Check for shared-cache-inval messages before trying to access the
1569 * relation. This is needed to cover the case where the name
1570 * identifies a rel that has been dropped and recreated since the
1571 * start of our transaction: if we don't flush the old syscache entry,
1572 * then we'll latch onto that entry and suffer an error later.
1574 AcceptInvalidationMessages();
1576 /* Look up the appropriate relation using namespace search. */
1577 state
.expected_relkind
= relkind
;
1578 state
.heap_lockmode
= drop
->concurrent
?
1579 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1580 /* We must initialize these fields to show that no locks are held: */
1581 state
.heapOid
= InvalidOid
;
1582 state
.partParentOid
= InvalidOid
;
1584 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1585 RangeVarCallbackForDropRelation
,
1589 if (!OidIsValid(relOid
))
1591 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1596 * Decide if concurrent mode needs to be used here or not. The
1597 * callback retrieved the rel's persistence for us.
1599 if (drop
->concurrent
&&
1600 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1602 Assert(list_length(drop
->objects
) == 1 &&
1603 drop
->removeType
== OBJECT_INDEX
);
1604 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1608 * Concurrent index drop cannot be used with partitioned indexes,
1611 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1612 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1614 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1615 errmsg("cannot drop partitioned index \"%s\" concurrently",
1619 * If we're told to drop a partitioned index, we must acquire lock on
1620 * all the children of its parent partitioned table before proceeding.
1621 * Otherwise we'd try to lock the child index partitions before their
1622 * tables, leading to potential deadlock against other sessions that
1623 * will lock those objects in the other order.
1625 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1626 (void) find_all_inheritors(state
.heapOid
,
1627 state
.heap_lockmode
,
1630 /* OK, we're ready to delete this one */
1631 obj
.classId
= RelationRelationId
;
1632 obj
.objectId
= relOid
;
1633 obj
.objectSubId
= 0;
1635 add_exact_object_address(&obj
, objects
);
1638 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1640 free_object_addresses(objects
);
1644 * Before acquiring a table lock, check whether we have sufficient rights.
1645 * In the case of DROP INDEX, also try to lock the table before the index.
1646 * Also, if the table to be dropped is a partition, we try to lock the parent
1650 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1654 struct DropRelationCallbackState
*state
;
1655 char expected_relkind
;
1657 Form_pg_class classform
;
1658 LOCKMODE heap_lockmode
;
1659 bool invalid_system_index
= false;
1661 state
= (struct DropRelationCallbackState
*) arg
;
1662 heap_lockmode
= state
->heap_lockmode
;
1665 * If we previously locked some other index's heap, and the name we're
1666 * looking up no longer refers to that relation, release the now-useless
1669 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1671 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1672 state
->heapOid
= InvalidOid
;
1676 * Similarly, if we previously locked some other partition's heap, and the
1677 * name we're looking up no longer refers to that relation, release the
1680 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1682 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1683 state
->partParentOid
= InvalidOid
;
1686 /* Didn't find a relation, so no need for locking or permission checks. */
1687 if (!OidIsValid(relOid
))
1690 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1691 if (!HeapTupleIsValid(tuple
))
1692 return; /* concurrently dropped, so nothing to do */
1693 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1694 is_partition
= classform
->relispartition
;
1696 /* Pass back some data to save lookups in RemoveRelations */
1697 state
->actual_relkind
= classform
->relkind
;
1698 state
->actual_relpersistence
= classform
->relpersistence
;
1701 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1702 * but RemoveRelations() can only pass one relkind for a given relation.
1703 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1704 * That means we must be careful before giving the wrong type error when
1705 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1706 * exists with indexes.
1708 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1709 expected_relkind
= RELKIND_RELATION
;
1710 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1711 expected_relkind
= RELKIND_INDEX
;
1713 expected_relkind
= classform
->relkind
;
1715 if (state
->expected_relkind
!= expected_relkind
)
1716 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1717 state
->expected_relkind
);
1719 /* Allow DROP to either table owner or schema owner */
1720 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1721 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1722 aclcheck_error(ACLCHECK_NOT_OWNER
,
1723 get_relkind_objtype(classform
->relkind
),
1727 * Check the case of a system index that might have been invalidated by a
1728 * failed concurrent process and allow its drop. For the time being, this
1729 * only concerns indexes of toast relations that became invalid during a
1730 * REINDEX CONCURRENTLY process.
1732 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1735 Form_pg_index indexform
;
1738 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1739 if (!HeapTupleIsValid(locTuple
))
1741 ReleaseSysCache(tuple
);
1745 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1746 indisvalid
= indexform
->indisvalid
;
1747 ReleaseSysCache(locTuple
);
1749 /* Mark object as being an invalid index of system catalogs */
1751 invalid_system_index
= true;
1754 /* In the case of an invalid index, it is fine to bypass this check */
1755 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1757 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1758 errmsg("permission denied: \"%s\" is a system catalog",
1761 ReleaseSysCache(tuple
);
1764 * In DROP INDEX, attempt to acquire lock on the parent table before
1765 * locking the index. index_drop() will need this anyway, and since
1766 * regular queries lock tables before their indexes, we risk deadlock if
1767 * we do it the other way around. No error if we don't find a pg_index
1768 * entry, though --- the relation may have been dropped. Note that this
1769 * code will execute for either plain or partitioned indexes.
1771 if (expected_relkind
== RELKIND_INDEX
&&
1772 relOid
!= oldRelOid
)
1774 state
->heapOid
= IndexGetRelation(relOid
, true);
1775 if (OidIsValid(state
->heapOid
))
1776 LockRelationOid(state
->heapOid
, heap_lockmode
);
1780 * Similarly, if the relation is a partition, we must acquire lock on its
1781 * parent before locking the partition. That's because queries lock the
1782 * parent before its partitions, so we risk deadlock if we do it the other
1785 if (is_partition
&& relOid
!= oldRelOid
)
1787 state
->partParentOid
= get_partition_parent(relOid
, true);
1788 if (OidIsValid(state
->partParentOid
))
1789 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1795 * Executes a TRUNCATE command.
1797 * This is a multi-relation truncate. We first open and grab exclusive
1798 * lock on all relations involved, checking permissions and otherwise
1799 * verifying that the relation is OK for truncation. Note that if relations
1800 * are foreign tables, at this stage, we have not yet checked that their
1801 * foreign data in external data sources are OK for truncation. These are
1802 * checked when foreign data are actually truncated later. In CASCADE mode,
1803 * relations having FK references to the targeted relations are automatically
1804 * added to the group; in RESTRICT mode, we check that all FK references are
1805 * internal to the group that's being truncated. Finally all the relations
1806 * are truncated and reindexed.
1809 ExecuteTruncate(TruncateStmt
*stmt
)
1813 List
*relids_logged
= NIL
;
1817 * Open, exclusive-lock, and check all the explicitly-specified relations
1819 foreach(cell
, stmt
->relations
)
1821 RangeVar
*rv
= lfirst(cell
);
1823 bool recurse
= rv
->inh
;
1825 LOCKMODE lockmode
= AccessExclusiveLock
;
1827 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1828 0, RangeVarCallbackForTruncate
,
1831 /* don't throw error for "TRUNCATE foo, foo" */
1832 if (list_member_oid(relids
, myrelid
))
1835 /* open the relation, we already hold a lock on it */
1836 rel
= table_open(myrelid
, NoLock
);
1839 * RangeVarGetRelidExtended() has done most checks with its callback,
1840 * but other checks with the now-opened Relation remain.
1842 truncate_check_activity(rel
);
1844 rels
= lappend(rels
, rel
);
1845 relids
= lappend_oid(relids
, myrelid
);
1847 /* Log this relation only if needed for logical decoding */
1848 if (RelationIsLogicallyLogged(rel
))
1849 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1856 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1858 foreach(child
, children
)
1860 Oid childrelid
= lfirst_oid(child
);
1862 if (list_member_oid(relids
, childrelid
))
1865 /* find_all_inheritors already got lock */
1866 rel
= table_open(childrelid
, NoLock
);
1869 * It is possible that the parent table has children that are
1870 * temp tables of other backends. We cannot safely access
1871 * such tables (because of buffering issues), and the best
1872 * thing to do is to silently ignore them. Note that this
1873 * check is the same as one of the checks done in
1874 * truncate_check_activity() called below, still it is kept
1875 * here for simplicity.
1877 if (RELATION_IS_OTHER_TEMP(rel
))
1879 table_close(rel
, lockmode
);
1884 * Inherited TRUNCATE commands perform access permission
1885 * checks on the parent table only. So we skip checking the
1886 * children's permissions and don't call
1887 * truncate_check_perms() here.
1889 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1890 truncate_check_activity(rel
);
1892 rels
= lappend(rels
, rel
);
1893 relids
= lappend_oid(relids
, childrelid
);
1895 /* Log this relation only if needed for logical decoding */
1896 if (RelationIsLogicallyLogged(rel
))
1897 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1900 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1902 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1903 errmsg("cannot truncate only a partitioned table"),
1904 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1907 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1908 stmt
->behavior
, stmt
->restart_seqs
, false);
1910 /* And close the rels */
1913 Relation rel
= (Relation
) lfirst(cell
);
1915 table_close(rel
, NoLock
);
1920 * ExecuteTruncateGuts
1922 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1923 * command (see above) as well as replication subscribers that execute a
1924 * replicated TRUNCATE action.
1926 * explicit_rels is the list of Relations to truncate that the command
1927 * specified. relids is the list of Oids corresponding to explicit_rels.
1928 * relids_logged is the list of Oids (a subset of relids) that require
1929 * WAL-logging. This is all a bit redundant, but the existing callers have
1930 * this information handy in this form.
1933 ExecuteTruncateGuts(List
*explicit_rels
,
1935 List
*relids_logged
,
1936 DropBehavior behavior
, bool restart_seqs
,
1937 bool run_as_table_owner
)
1940 List
*seq_relids
= NIL
;
1941 HTAB
*ft_htab
= NULL
;
1943 ResultRelInfo
*resultRelInfos
;
1944 ResultRelInfo
*resultRelInfo
;
1945 SubTransactionId mySubid
;
1950 * Check the explicitly-specified relations.
1952 * In CASCADE mode, suck in all referencing relations as well. This
1953 * requires multiple iterations to find indirectly-dependent relations. At
1954 * each phase, we need to exclusive-lock new rels before looking for their
1955 * dependencies, else we might miss something. Also, we check each rel as
1956 * soon as we open it, to avoid a faux pas such as holding lock for a long
1957 * time on a rel we have no permissions for.
1959 rels
= list_copy(explicit_rels
);
1960 if (behavior
== DROP_CASCADE
)
1966 newrelids
= heap_truncate_find_FKs(relids
);
1967 if (newrelids
== NIL
)
1968 break; /* nothing else to add */
1970 foreach(cell
, newrelids
)
1972 Oid relid
= lfirst_oid(cell
);
1975 rel
= table_open(relid
, AccessExclusiveLock
);
1977 (errmsg("truncate cascades to table \"%s\"",
1978 RelationGetRelationName(rel
))));
1979 truncate_check_rel(relid
, rel
->rd_rel
);
1980 truncate_check_perms(relid
, rel
->rd_rel
);
1981 truncate_check_activity(rel
);
1982 rels
= lappend(rels
, rel
);
1983 relids
= lappend_oid(relids
, relid
);
1985 /* Log this relation only if needed for logical decoding */
1986 if (RelationIsLogicallyLogged(rel
))
1987 relids_logged
= lappend_oid(relids_logged
, relid
);
1993 * Check foreign key references. In CASCADE mode, this should be
1994 * unnecessary since we just pulled in all the references; but as a
1995 * cross-check, do it anyway if in an Assert-enabled build.
1997 #ifdef USE_ASSERT_CHECKING
1998 heap_truncate_check_FKs(rels
, false);
2000 if (behavior
== DROP_RESTRICT
)
2001 heap_truncate_check_FKs(rels
, false);
2005 * If we are asked to restart sequences, find all the sequences, lock them
2006 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2007 * We want to do this early since it's pointless to do all the truncation
2008 * work only to fail on sequence permissions.
2014 Relation rel
= (Relation
) lfirst(cell
);
2015 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
2018 foreach(seqcell
, seqlist
)
2020 Oid seq_relid
= lfirst_oid(seqcell
);
2023 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
2025 /* This check must match AlterSequence! */
2026 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
2027 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
2028 RelationGetRelationName(seq_rel
));
2030 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
2032 relation_close(seq_rel
, NoLock
);
2037 /* Prepare to catch AFTER triggers. */
2038 AfterTriggerBeginQuery();
2041 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2042 * each relation. We don't need to call ExecOpenIndices, though.
2044 * We put the ResultRelInfos in the es_opened_result_relations list, even
2045 * though we don't have a range table and don't populate the
2046 * es_result_relations array. That's a bit bogus, but it's enough to make
2047 * ExecGetTriggerResultRel() find them.
2049 estate
= CreateExecutorState();
2050 resultRelInfos
= (ResultRelInfo
*)
2051 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
2052 resultRelInfo
= resultRelInfos
;
2055 Relation rel
= (Relation
) lfirst(cell
);
2057 InitResultRelInfo(resultRelInfo
,
2059 0, /* dummy rangetable index */
2062 estate
->es_opened_result_relations
=
2063 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
2068 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2069 * truncating (this is because one of them might throw an error). Also, if
2070 * we were to allow them to prevent statement execution, that would need
2071 * to be handled here.
2073 resultRelInfo
= resultRelInfos
;
2078 if (run_as_table_owner
)
2079 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2081 ExecBSTruncateTriggers(estate
, resultRelInfo
);
2082 if (run_as_table_owner
)
2083 RestoreUserContext(&ucxt
);
2088 * OK, truncate each table.
2090 mySubid
= GetCurrentSubTransactionId();
2094 Relation rel
= (Relation
) lfirst(cell
);
2096 /* Skip partitioned tables as there is nothing to do */
2097 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
2101 * Build the lists of foreign tables belonging to each foreign server
2102 * and pass each list to the foreign data wrapper's callback function,
2103 * so that each server can truncate its all foreign tables in bulk.
2104 * Each list is saved as a single entry in a hash table that uses the
2105 * server OID as lookup key.
2107 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
2109 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
2111 ForeignTruncateInfo
*ft_info
;
2113 /* First time through, initialize hashtable for foreign tables */
2118 memset(&hctl
, 0, sizeof(HASHCTL
));
2119 hctl
.keysize
= sizeof(Oid
);
2120 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
2121 hctl
.hcxt
= CurrentMemoryContext
;
2123 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
2124 32, /* start small and extend */
2126 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
2129 /* Find or create cached entry for the foreign table */
2130 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
2132 ft_info
->rels
= NIL
;
2135 * Save the foreign table in the entry of the server that the
2136 * foreign table belongs to.
2138 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2143 * Normally, we need a transaction-safe truncation here. However, if
2144 * the table was either created in the current (sub)transaction or has
2145 * a new relfilenumber in the current (sub)transaction, then we can
2146 * just truncate it in-place, because a rollback would cause the whole
2147 * table or the current physical file to be thrown away anyway.
2149 if (rel
->rd_createSubid
== mySubid
||
2150 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2152 /* Immediate, non-rollbackable truncation is OK */
2153 heap_truncate_one_rel(rel
);
2159 ReindexParams reindex_params
= {0};
2162 * This effectively deletes all rows in the table, and may be done
2163 * in a serializable transaction. In that case we must record a
2164 * rw-conflict in to this transaction from each transaction
2165 * holding a predicate lock on the table.
2167 CheckTableForSerializableConflictIn(rel
);
2170 * Need the full transaction-safe pushups.
2172 * Create a new empty storage file for the relation, and assign it
2173 * as the relfilenumber value. The old storage file is scheduled
2174 * for deletion at commit.
2176 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2178 heap_relid
= RelationGetRelid(rel
);
2181 * The same for the toast table, if any.
2183 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2184 if (OidIsValid(toast_relid
))
2186 Relation toastrel
= relation_open(toast_relid
,
2187 AccessExclusiveLock
);
2189 RelationSetNewRelfilenumber(toastrel
,
2190 toastrel
->rd_rel
->relpersistence
);
2191 table_close(toastrel
, NoLock
);
2195 * Reconstruct the indexes to match, and we're done.
2197 reindex_relation(NULL
, heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2201 pgstat_count_truncate(rel
);
2204 /* Now go through the hash table, and truncate foreign tables */
2207 ForeignTruncateInfo
*ft_info
;
2208 HASH_SEQ_STATUS seq
;
2210 hash_seq_init(&seq
, ft_htab
);
2214 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2216 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2218 /* truncate_check_rel() has checked that already */
2219 Assert(routine
->ExecForeignTruncate
!= NULL
);
2221 routine
->ExecForeignTruncate(ft_info
->rels
,
2228 hash_destroy(ft_htab
);
2234 * Restart owned sequences if we were asked to.
2236 foreach(cell
, seq_relids
)
2238 Oid seq_relid
= lfirst_oid(cell
);
2240 ResetSequence(seq_relid
);
2244 * Write a WAL record to allow this set of actions to be logically
2247 * Assemble an array of relids so we can write a single WAL record for the
2250 if (relids_logged
!= NIL
)
2252 xl_heap_truncate xlrec
;
2255 /* should only get here if wal_level >= logical */
2256 Assert(XLogLogicalInfoActive());
2258 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2259 foreach(cell
, relids_logged
)
2260 logrelids
[i
++] = lfirst_oid(cell
);
2262 xlrec
.dbId
= MyDatabaseId
;
2263 xlrec
.nrelids
= list_length(relids_logged
);
2265 if (behavior
== DROP_CASCADE
)
2266 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2268 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2271 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2272 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2274 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2276 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2280 * Process all AFTER STATEMENT TRUNCATE triggers.
2282 resultRelInfo
= resultRelInfos
;
2287 if (run_as_table_owner
)
2288 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2290 ExecASTruncateTriggers(estate
, resultRelInfo
);
2291 if (run_as_table_owner
)
2292 RestoreUserContext(&ucxt
);
2296 /* Handle queued AFTER triggers */
2297 AfterTriggerEndQuery(estate
);
2299 /* We can clean up the EState now */
2300 FreeExecutorState(estate
);
2303 * Close any rels opened by CASCADE (can't do this while EState still
2306 rels
= list_difference_ptr(rels
, explicit_rels
);
2309 Relation rel
= (Relation
) lfirst(cell
);
2311 table_close(rel
, NoLock
);
2316 * Check that a given relation is safe to truncate. Subroutine for
2317 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2320 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2322 char *relname
= NameStr(reltuple
->relname
);
2325 * Only allow truncate on regular tables, foreign tables using foreign
2326 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2327 * latter are only being included here for the following checks; no
2328 * physical truncation will occur in their case.).
2330 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2332 Oid serverid
= GetForeignServerIdByRelId(relid
);
2333 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2335 if (!fdwroutine
->ExecForeignTruncate
)
2337 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2338 errmsg("cannot truncate foreign table \"%s\"",
2341 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2342 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2344 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2345 errmsg("\"%s\" is not a table", relname
)));
2348 * Most system catalogs can't be truncated at all, or at least not unless
2349 * allow_system_table_mods=on. As an exception, however, we allow
2350 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2351 * to change its relfilenode to match the old cluster, and allowing a
2352 * TRUNCATE command to be executed is the easiest way of doing that.
2354 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2355 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2357 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2358 errmsg("permission denied: \"%s\" is a system catalog",
2361 InvokeObjectTruncateHook(relid
);
2365 * Check that current user has the permission to truncate given relation.
2368 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2370 char *relname
= NameStr(reltuple
->relname
);
2371 AclResult aclresult
;
2373 /* Permissions checks */
2374 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2375 if (aclresult
!= ACLCHECK_OK
)
2376 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2381 * Set of extra sanity checks to check if a given relation is safe to
2382 * truncate. This is split with truncate_check_rel() as
2383 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2386 truncate_check_activity(Relation rel
)
2389 * Don't allow truncate on temp tables of other backends ... their local
2390 * buffer manager is not going to cope.
2392 if (RELATION_IS_OTHER_TEMP(rel
))
2394 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2395 errmsg("cannot truncate temporary tables of other sessions")));
2398 * Also check for active uses of the relation in the current transaction,
2399 * including open scans and pending AFTER trigger events.
2401 CheckTableNotInUse(rel
, "TRUNCATE");
2406 * returns the name corresponding to a typstorage/attstorage enum value
2409 storage_name(char c
)
2413 case TYPSTORAGE_PLAIN
:
2415 case TYPSTORAGE_EXTERNAL
:
2417 case TYPSTORAGE_EXTENDED
:
2419 case TYPSTORAGE_MAIN
:
2428 * Returns new schema given initial schema and superclasses.
2431 * 'columns' is the column/attribute definition for the table. (It's a list
2432 * of ColumnDef's.) It is destructively changed.
2433 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2434 * 'relpersistence' is the persistence type of the table.
2435 * 'is_partition' tells if the table is a partition.
2438 * 'supconstr' receives a list of constraints belonging to the parents,
2439 * updated as necessary to be valid for the child.
2440 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2441 * constraints coming from inheritance parents.
2444 * Completed schema list.
2447 * The order in which the attributes are inherited is very important.
2448 * Intuitively, the inherited attributes should come first. If a table
2449 * inherits from multiple parents, the order of those attributes are
2450 * according to the order of the parents specified in CREATE TABLE.
2452 * Here's an example:
2454 * create table person (name text, age int4, location point);
2455 * create table emp (salary int4, manager text) inherits(person);
2456 * create table student (gpa float8) inherits (person);
2457 * create table stud_emp (percent int4) inherits (emp, student);
2459 * The order of the attributes of stud_emp is:
2461 * person {1:name, 2:age, 3:location}
2463 * {6:gpa} student emp {4:salary, 5:manager}
2465 * stud_emp {7:percent}
2467 * If the same attribute name appears multiple times, then it appears
2468 * in the result table in the proper location for its first appearance.
2470 * Constraints (including not-null constraints) for the child table
2471 * are the union of all relevant constraints, from both the child schema
2472 * and parent tables. In addition, in legacy inheritance, each column that
2473 * appears in a primary key in any of the parents also gets a NOT NULL
2474 * constraint (partitioning doesn't need this, because the PK itself gets
2477 * The default value for a child column is defined as:
2478 * (1) If the child schema specifies a default, that value is used.
2479 * (2) If neither the child nor any parent specifies a default, then
2480 * the column will not have a default.
2481 * (3) If conflicting defaults are inherited from different parents
2482 * (and not overridden by the child), an error is raised.
2483 * (4) Otherwise the inherited default is used.
2485 * Note that the default-value infrastructure is used for generated
2486 * columns' expressions too, so most of the preceding paragraph applies
2487 * to generation expressions too. We insist that a child column be
2488 * generated if and only if its parent(s) are, but it need not have
2489 * the same generation expression.
2493 MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
2494 bool is_partition
, List
**supconstr
, List
**supnotnulls
)
2496 List
*inh_columns
= NIL
;
2497 List
*constraints
= NIL
;
2498 List
*nnconstraints
= NIL
;
2499 bool have_bogus_defaults
= false;
2501 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2502 List
*saved_columns
= NIL
;
2506 * Check for and reject tables with too many columns. We perform this
2507 * check relatively early for two reasons: (a) we don't run the risk of
2508 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2509 * okay if we're processing <= 1600 columns, but could take minutes to
2510 * execute if the user attempts to create a table with hundreds of
2511 * thousands of columns.
2513 * Note that we also need to check that we do not exceed this figure after
2514 * including columns from inherited relations.
2516 if (list_length(columns
) > MaxHeapAttributeNumber
)
2518 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2519 errmsg("tables can have at most %d columns",
2520 MaxHeapAttributeNumber
)));
2523 * Check for duplicate names in the explicit list of attributes.
2525 * Although we might consider merging such entries in the same way that we
2526 * handle name conflicts for inherited attributes, it seems to make more
2527 * sense to assume such conflicts are errors.
2529 * We don't use foreach() here because we have two nested loops over the
2530 * columns list, with possible element deletions in the inner one. If we
2531 * used foreach_delete_current() it could only fix up the state of one of
2532 * the loops, so it seems cleaner to use looping over list indexes for
2533 * both loops. Note that any deletion will happen beyond where the outer
2534 * loop is, so its index never needs adjustment.
2536 for (int coldefpos
= 0; coldefpos
< list_length(columns
); coldefpos
++)
2538 ColumnDef
*coldef
= list_nth_node(ColumnDef
, columns
, coldefpos
);
2540 if (!is_partition
&& coldef
->typeName
== NULL
)
2543 * Typed table column option that does not belong to a column from
2544 * the type. This works because the columns from the type come
2545 * first in the list. (We omit this check for partition column
2546 * lists; those are processed separately below.)
2549 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2550 errmsg("column \"%s\" does not exist",
2554 /* restpos scans all entries beyond coldef; incr is in loop body */
2555 for (int restpos
= coldefpos
+ 1; restpos
< list_length(columns
);)
2557 ColumnDef
*restdef
= list_nth_node(ColumnDef
, columns
, restpos
);
2559 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2561 if (coldef
->is_from_type
)
2564 * merge the column options into the column from the type
2566 coldef
->is_not_null
= restdef
->is_not_null
;
2567 coldef
->raw_default
= restdef
->raw_default
;
2568 coldef
->cooked_default
= restdef
->cooked_default
;
2569 coldef
->constraints
= restdef
->constraints
;
2570 coldef
->is_from_type
= false;
2571 columns
= list_delete_nth_cell(columns
, restpos
);
2575 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2576 errmsg("column \"%s\" specified more than once",
2585 * In case of a partition, there are no new column definitions, only dummy
2586 * ColumnDefs created for column constraints. Set them aside for now and
2587 * process them at the end.
2591 saved_columns
= columns
;
2596 * Scan the parents left-to-right, and merge their attributes to form a
2597 * list of inherited columns (inh_columns).
2602 Oid parent
= lfirst_oid(lc
);
2604 TupleDesc tupleDesc
;
2605 TupleConstr
*constr
;
2607 List
*inherited_defaults
;
2608 List
*cols_with_defaults
;
2613 Bitmapset
*nncols
= NULL
;
2615 /* caller already got lock */
2616 relation
= table_open(parent
, NoLock
);
2619 * Check for active uses of the parent partitioned table in the
2620 * current transaction, such as being used in some manner by an
2621 * enclosing command.
2624 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2627 * We do not allow partitioned tables and partitions to participate in
2628 * regular inheritance.
2630 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !is_partition
)
2632 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2633 errmsg("cannot inherit from partitioned table \"%s\"",
2634 RelationGetRelationName(relation
))));
2635 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2637 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2638 errmsg("cannot inherit from partition \"%s\"",
2639 RelationGetRelationName(relation
))));
2641 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2642 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2643 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2645 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2646 errmsg("inherited relation \"%s\" is not a table or foreign table",
2647 RelationGetRelationName(relation
))));
2650 * If the parent is permanent, so must be all of its partitions. Note
2651 * that inheritance allows that case.
2654 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2655 relpersistence
== RELPERSISTENCE_TEMP
)
2657 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2658 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2659 RelationGetRelationName(relation
))));
2661 /* Permanent rels cannot inherit from temporary ones */
2662 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2663 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2665 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2666 errmsg(!is_partition
2667 ? "cannot inherit from temporary relation \"%s\""
2668 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2669 RelationGetRelationName(relation
))));
2671 /* If existing rel is temp, it must belong to this session */
2672 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2673 !relation
->rd_islocaltemp
)
2675 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2676 errmsg(!is_partition
2677 ? "cannot inherit from temporary relation of another session"
2678 : "cannot create as partition of temporary relation of another session")));
2681 * We should have an UNDER permission flag for this, but for now,
2682 * demand that creator of a child table own the parent.
2684 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2685 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2686 RelationGetRelationName(relation
));
2688 tupleDesc
= RelationGetDescr(relation
);
2689 constr
= tupleDesc
->constr
;
2692 * newattmap->attnums[] will contain the child-table attribute numbers
2693 * for the attributes of this parent table. (They are not the same
2694 * for parents after the first one, nor if we have dropped columns.)
2696 newattmap
= make_attrmap(tupleDesc
->natts
);
2698 /* We can't process inherited defaults until newattmap is complete. */
2699 inherited_defaults
= cols_with_defaults
= NIL
;
2702 * All columns that are part of the parent's primary key need to be
2703 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2704 * constraint (if they don't have one already). Also, we request
2705 * attnotnull on columns that have a not-null constraint that's not
2706 * marked NO INHERIT.
2708 pkattrs
= RelationGetIndexAttrBitmap(relation
,
2709 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
2710 nnconstrs
= RelationGetNotNullConstraints(RelationGetRelid(relation
), true);
2711 foreach(lc1
, nnconstrs
)
2712 nncols
= bms_add_member(nncols
,
2713 ((CookedConstraint
*) lfirst(lc1
))->attnum
);
2715 for (AttrNumber parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2718 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2720 char *attributeName
= NameStr(attribute
->attname
);
2723 ColumnDef
*mergeddef
;
2726 * Ignore dropped columns in the parent.
2728 if (attribute
->attisdropped
)
2729 continue; /* leave newattmap->attnums entry as zero */
2732 * Create new column definition
2734 newdef
= makeColumnDef(attributeName
, attribute
->atttypid
,
2735 attribute
->atttypmod
, attribute
->attcollation
);
2736 newdef
->storage
= attribute
->attstorage
;
2737 newdef
->generated
= attribute
->attgenerated
;
2738 if (CompressionMethodIsValid(attribute
->attcompression
))
2739 newdef
->compression
=
2740 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2743 * Regular inheritance children are independent enough not to
2744 * inherit identity columns. But partitions are integral part of
2745 * a partitioned table and inherit identity column.
2748 newdef
->identity
= attribute
->attidentity
;
2751 * Does it match some previously considered column from another
2754 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2755 if (exist_attno
> 0)
2758 * Yes, try to merge the two column definitions.
2760 mergeddef
= MergeInheritedAttribute(inh_columns
, exist_attno
, newdef
);
2762 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2765 * Partitions have only one parent, so conflict should never
2768 Assert(!is_partition
);
2773 * No, create a new inherited column
2775 newdef
->inhcount
= 1;
2776 newdef
->is_local
= false;
2777 inh_columns
= lappend(inh_columns
, newdef
);
2779 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2785 * mark attnotnull if parent has it and it's not NO INHERIT
2787 if (bms_is_member(parent_attno
, nncols
) ||
2788 bms_is_member(parent_attno
- FirstLowInvalidHeapAttributeNumber
,
2790 mergeddef
->is_not_null
= true;
2793 * In regular inheritance, columns in the parent's primary key get
2794 * an extra not-null constraint. Partitioning doesn't need this,
2795 * because the PK itself is going to be cloned to the partition.
2797 if (!is_partition
&&
2798 bms_is_member(parent_attno
-
2799 FirstLowInvalidHeapAttributeNumber
,
2802 CookedConstraint
*nn
;
2804 nn
= palloc(sizeof(CookedConstraint
));
2805 nn
->contype
= CONSTR_NOTNULL
;
2806 nn
->conoid
= InvalidOid
;
2808 nn
->attnum
= newattmap
->attnums
[parent_attno
- 1];
2810 nn
->skip_validation
= false;
2811 nn
->is_local
= false;
2813 nn
->is_no_inherit
= false;
2815 nnconstraints
= lappend(nnconstraints
, nn
);
2819 * Locate default/generation expression if any
2821 if (attribute
->atthasdef
)
2825 this_default
= TupleDescGetDefault(tupleDesc
, parent_attno
);
2826 if (this_default
== NULL
)
2827 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2828 parent_attno
, RelationGetRelationName(relation
));
2831 * If it's a GENERATED default, it might contain Vars that
2832 * need to be mapped to the inherited column(s)' new numbers.
2833 * We can't do that till newattmap is ready, so just remember
2834 * all the inherited default expressions for the moment.
2836 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2837 cols_with_defaults
= lappend(cols_with_defaults
, mergeddef
);
2842 * Now process any inherited default expressions, adjusting attnos
2843 * using the completed newattmap map.
2845 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2847 Node
*this_default
= (Node
*) lfirst(lc1
);
2848 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2849 bool found_whole_row
;
2851 /* Adjust Vars to match new table's column numbering */
2852 this_default
= map_variable_attnos(this_default
,
2855 InvalidOid
, &found_whole_row
);
2858 * For the moment we have to reject whole-row variables. We could
2859 * convert them, if we knew the new table's rowtype OID, but that
2860 * hasn't been assigned yet. (A variable could only appear in a
2861 * generation expression, so the error message is correct.)
2863 if (found_whole_row
)
2865 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2866 errmsg("cannot convert whole-row table reference"),
2867 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2869 RelationGetRelationName(relation
))));
2872 * If we already had a default from some prior parent, check to
2873 * see if they are the same. If so, no problem; if not, mark the
2874 * column as having a bogus default. Below, we will complain if
2875 * the bogus default isn't overridden by the child columns.
2877 Assert(def
->raw_default
== NULL
);
2878 if (def
->cooked_default
== NULL
)
2879 def
->cooked_default
= this_default
;
2880 else if (!equal(def
->cooked_default
, this_default
))
2882 def
->cooked_default
= &bogus_marker
;
2883 have_bogus_defaults
= true;
2888 * Now copy the CHECK constraints of this parent, adjusting attnos
2889 * using the completed newattmap map. Identically named constraints
2890 * are merged if possible, else we throw error.
2892 if (constr
&& constr
->num_check
> 0)
2894 ConstrCheck
*check
= constr
->check
;
2896 for (int i
= 0; i
< constr
->num_check
; i
++)
2898 char *name
= check
[i
].ccname
;
2900 bool found_whole_row
;
2902 /* ignore if the constraint is non-inheritable */
2903 if (check
[i
].ccnoinherit
)
2906 /* Adjust Vars to match new table's column numbering */
2907 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2910 InvalidOid
, &found_whole_row
);
2913 * For the moment we have to reject whole-row variables. We
2914 * could convert them, if we knew the new table's rowtype OID,
2915 * but that hasn't been assigned yet.
2917 if (found_whole_row
)
2919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2920 errmsg("cannot convert whole-row table reference"),
2921 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2923 RelationGetRelationName(relation
))));
2925 constraints
= MergeCheckConstraint(constraints
, name
, expr
);
2930 * Also copy the not-null constraints from this parent. The
2931 * attnotnull markings were already installed above.
2933 foreach(lc1
, nnconstrs
)
2935 CookedConstraint
*nn
= lfirst(lc1
);
2937 Assert(nn
->contype
== CONSTR_NOTNULL
);
2939 nn
->attnum
= newattmap
->attnums
[nn
->attnum
- 1];
2940 nn
->is_local
= false;
2943 nnconstraints
= lappend(nnconstraints
, nn
);
2946 free_attrmap(newattmap
);
2949 * Close the parent rel, but keep our lock on it until xact commit.
2950 * That will prevent someone else from deleting or ALTERing the parent
2951 * before the child is committed.
2953 table_close(relation
, NoLock
);
2957 * If we had no inherited attributes, the result columns are just the
2958 * explicitly declared columns. Otherwise, we need to merge the declared
2959 * columns into the inherited column list. Although, we never have any
2960 * explicitly declared columns if the table is a partition.
2962 if (inh_columns
!= NIL
)
2964 int newcol_attno
= 0;
2966 foreach(lc
, columns
)
2968 ColumnDef
*newdef
= lfirst_node(ColumnDef
, lc
);
2969 char *attributeName
= newdef
->colname
;
2973 * Partitions have only one parent and have no column definitions
2974 * of their own, so conflict should never occur.
2976 Assert(!is_partition
);
2981 * Does it match some inherited column?
2983 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2984 if (exist_attno
> 0)
2987 * Yes, try to merge the two column definitions.
2989 MergeChildAttribute(inh_columns
, exist_attno
, newcol_attno
, newdef
);
2994 * No, attach new column unchanged to result columns.
2996 inh_columns
= lappend(inh_columns
, newdef
);
3000 columns
= inh_columns
;
3003 * Check that we haven't exceeded the legal # of columns after merging
3004 * in inherited columns.
3006 if (list_length(columns
) > MaxHeapAttributeNumber
)
3008 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
3009 errmsg("tables can have at most %d columns",
3010 MaxHeapAttributeNumber
)));
3014 * Now that we have the column definition list for a partition, we can
3015 * check whether the columns referenced in the column constraint specs
3016 * actually exist. Also, merge column defaults.
3020 foreach(lc
, saved_columns
)
3022 ColumnDef
*restdef
= lfirst(lc
);
3028 ColumnDef
*coldef
= lfirst(l
);
3030 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
3035 * Check for conflicts related to generated columns.
3037 * Same rules as above: generated-ness has to match the
3038 * parent, but the contents of the generation expression
3041 if (coldef
->generated
)
3043 if (restdef
->raw_default
&& !restdef
->generated
)
3045 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3046 errmsg("column \"%s\" inherits from generated column but specifies default",
3047 restdef
->colname
)));
3048 if (restdef
->identity
)
3050 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3051 errmsg("column \"%s\" inherits from generated column but specifies identity",
3052 restdef
->colname
)));
3056 if (restdef
->generated
)
3058 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3059 errmsg("child column \"%s\" specifies generation expression",
3061 errhint("A child table column cannot be generated unless its parent column is.")));
3065 * Override the parent's default value for this column
3066 * (coldef->cooked_default) with the partition's local
3067 * definition (restdef->raw_default), if there's one. It
3068 * should be physically impossible to get a cooked default
3069 * in the local definition or a raw default in the
3070 * inherited definition, but make sure they're nulls, for
3073 Assert(restdef
->cooked_default
== NULL
);
3074 Assert(coldef
->raw_default
== NULL
);
3075 if (restdef
->raw_default
)
3077 coldef
->raw_default
= restdef
->raw_default
;
3078 coldef
->cooked_default
= NULL
;
3083 /* complain for constraints on columns not in parent */
3086 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3087 errmsg("column \"%s\" does not exist",
3088 restdef
->colname
)));
3093 * If we found any conflicting parent default values, check to make sure
3094 * they were overridden by the child.
3096 if (have_bogus_defaults
)
3098 foreach(lc
, columns
)
3100 ColumnDef
*def
= lfirst(lc
);
3102 if (def
->cooked_default
== &bogus_marker
)
3106 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3107 errmsg("column \"%s\" inherits conflicting generation expressions",
3109 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3112 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3113 errmsg("column \"%s\" inherits conflicting default values",
3115 errhint("To resolve the conflict, specify a default explicitly.")));
3120 *supconstr
= constraints
;
3121 *supnotnulls
= nnconstraints
;
3128 * MergeCheckConstraint
3129 * Try to merge an inherited CHECK constraint with previous ones
3131 * If we inherit identically-named constraints from multiple parents, we must
3132 * merge them, or throw an error if they don't have identical definitions.
3134 * constraints is a list of CookedConstraint structs for previous constraints.
3136 * If the new constraint matches an existing one, then the existing
3137 * constraint's inheritance count is updated. If there is a conflict (same
3138 * name but different expression), throw an error. If the constraint neither
3139 * matches nor conflicts with an existing one, a new constraint is appended to
3143 MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
)
3146 CookedConstraint
*newcon
;
3148 foreach(lc
, constraints
)
3150 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3152 Assert(ccon
->contype
== CONSTR_CHECK
);
3154 /* Non-matching names never conflict */
3155 if (strcmp(ccon
->name
, name
) != 0)
3158 if (equal(expr
, ccon
->expr
))
3160 /* OK to merge constraint with existing */
3162 if (ccon
->inhcount
< 0)
3164 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3165 errmsg("too many inheritance parents"));
3170 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3171 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3176 * Constraint couldn't be merged with an existing one and also didn't
3177 * conflict with an existing one, so add it as a new one to the list.
3179 newcon
= palloc0_object(CookedConstraint
);
3180 newcon
->contype
= CONSTR_CHECK
;
3181 newcon
->name
= pstrdup(name
);
3182 newcon
->expr
= expr
;
3183 newcon
->inhcount
= 1;
3184 return lappend(constraints
, newcon
);
3188 * MergeChildAttribute
3189 * Merge given child attribute definition into given inherited attribute.
3192 * 'inh_columns' is the list of inherited ColumnDefs.
3193 * 'exist_attno' is the number of the inherited attribute in inh_columns
3194 * 'newcol_attno' is the attribute number in child table's schema definition
3195 * 'newdef' is the column/attribute definition from the child table.
3197 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3198 * ColumnDef remains unchanged.
3201 * - The attribute is merged according to the rules laid out in the prologue
3202 * of MergeAttributes().
3203 * - If matching inherited attribute exists but the child attribute can not be
3204 * merged into it, the function throws respective errors.
3205 * - A partition can not have its own column definitions. Hence this function
3206 * is applicable only to a regular inheritance child.
3209 MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
)
3211 char *attributeName
= newdef
->colname
;
3220 if (exist_attno
== newcol_attno
)
3222 (errmsg("merging column \"%s\" with inherited definition",
3226 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
3227 errdetail("User-specified column moved to the position of the inherited column.")));
3229 inhdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3232 * Must have the same type and typmod
3234 typenameTypeIdAndMod(NULL
, inhdef
->typeName
, &inhtypeid
, &inhtypmod
);
3235 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3236 if (inhtypeid
!= newtypeid
|| inhtypmod
!= newtypmod
)
3238 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3239 errmsg("column \"%s\" has a type conflict",
3241 errdetail("%s versus %s",
3242 format_type_with_typemod(inhtypeid
, inhtypmod
),
3243 format_type_with_typemod(newtypeid
, newtypmod
))));
3246 * Must have the same collation
3248 inhcollid
= GetColumnDefCollation(NULL
, inhdef
, inhtypeid
);
3249 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3250 if (inhcollid
!= newcollid
)
3252 (errcode(ERRCODE_COLLATION_MISMATCH
),
3253 errmsg("column \"%s\" has a collation conflict",
3255 errdetail("\"%s\" versus \"%s\"",
3256 get_collation_name(inhcollid
),
3257 get_collation_name(newcollid
))));
3260 * Identity is never inherited by a regular inheritance child. Pick
3261 * child's identity definition if there's one.
3263 inhdef
->identity
= newdef
->identity
;
3266 * Copy storage parameter
3268 if (inhdef
->storage
== 0)
3269 inhdef
->storage
= newdef
->storage
;
3270 else if (newdef
->storage
!= 0 && inhdef
->storage
!= newdef
->storage
)
3272 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3273 errmsg("column \"%s\" has a storage parameter conflict",
3275 errdetail("%s versus %s",
3276 storage_name(inhdef
->storage
),
3277 storage_name(newdef
->storage
))));
3280 * Copy compression parameter
3282 if (inhdef
->compression
== NULL
)
3283 inhdef
->compression
= newdef
->compression
;
3284 else if (newdef
->compression
!= NULL
)
3286 if (strcmp(inhdef
->compression
, newdef
->compression
) != 0)
3288 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3289 errmsg("column \"%s\" has a compression method conflict",
3291 errdetail("%s versus %s", inhdef
->compression
, newdef
->compression
)));
3295 * Merge of not-null constraints = OR 'em together
3297 inhdef
->is_not_null
|= newdef
->is_not_null
;
3300 * Check for conflicts related to generated columns.
3302 * If the parent column is generated, the child column will be made a
3303 * generated column if it isn't already. If it is a generated column,
3304 * we'll take its generation expression in preference to the parent's. We
3305 * must check that the child column doesn't specify a default value or
3306 * identity, which matches the rules for a single column in
3309 * Conversely, if the parent column is not generated, the child column
3310 * can't be either. (We used to allow that, but it results in being able
3311 * to override the generation expression via UPDATEs through the parent.)
3313 if (inhdef
->generated
)
3315 if (newdef
->raw_default
&& !newdef
->generated
)
3317 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3318 errmsg("column \"%s\" inherits from generated column but specifies default",
3320 if (newdef
->identity
)
3322 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3323 errmsg("column \"%s\" inherits from generated column but specifies identity",
3328 if (newdef
->generated
)
3330 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3331 errmsg("child column \"%s\" specifies generation expression",
3333 errhint("A child table column cannot be generated unless its parent column is.")));
3337 * If new def has a default, override previous default
3339 if (newdef
->raw_default
!= NULL
)
3341 inhdef
->raw_default
= newdef
->raw_default
;
3342 inhdef
->cooked_default
= newdef
->cooked_default
;
3345 /* Mark the column as locally defined */
3346 inhdef
->is_local
= true;
3350 * MergeInheritedAttribute
3351 * Merge given parent attribute definition into specified attribute
3352 * inherited from the previous parents.
3355 * 'inh_columns' is the list of previously inherited ColumnDefs.
3356 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3357 * 'newdef' is the new parent column/attribute definition to be merged.
3359 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3362 * - The attribute is merged according to the rules laid out in the prologue
3363 * of MergeAttributes().
3364 * - If matching inherited attribute exists but the new attribute can not be
3365 * merged into it, the function throws respective errors.
3366 * - A partition inherits from only a single parent. Hence this function is
3367 * applicable only to a regular inheritance.
3370 MergeInheritedAttribute(List
*inh_columns
,
3372 const ColumnDef
*newdef
)
3374 char *attributeName
= newdef
->colname
;
3384 (errmsg("merging multiple inherited definitions of column \"%s\"",
3386 prevdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3389 * Must have the same type and typmod
3391 typenameTypeIdAndMod(NULL
, prevdef
->typeName
, &prevtypeid
, &prevtypmod
);
3392 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3393 if (prevtypeid
!= newtypeid
|| prevtypmod
!= newtypmod
)
3395 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3396 errmsg("inherited column \"%s\" has a type conflict",
3398 errdetail("%s versus %s",
3399 format_type_with_typemod(prevtypeid
, prevtypmod
),
3400 format_type_with_typemod(newtypeid
, newtypmod
))));
3403 * Must have the same collation
3405 prevcollid
= GetColumnDefCollation(NULL
, prevdef
, prevtypeid
);
3406 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3407 if (prevcollid
!= newcollid
)
3409 (errcode(ERRCODE_COLLATION_MISMATCH
),
3410 errmsg("inherited column \"%s\" has a collation conflict",
3412 errdetail("\"%s\" versus \"%s\"",
3413 get_collation_name(prevcollid
),
3414 get_collation_name(newcollid
))));
3417 * Copy/check storage parameter
3419 if (prevdef
->storage
== 0)
3420 prevdef
->storage
= newdef
->storage
;
3421 else if (prevdef
->storage
!= newdef
->storage
)
3423 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3424 errmsg("inherited column \"%s\" has a storage parameter conflict",
3426 errdetail("%s versus %s",
3427 storage_name(prevdef
->storage
),
3428 storage_name(newdef
->storage
))));
3431 * Copy/check compression parameter
3433 if (prevdef
->compression
== NULL
)
3434 prevdef
->compression
= newdef
->compression
;
3435 else if (newdef
->compression
!= NULL
)
3437 if (strcmp(prevdef
->compression
, newdef
->compression
) != 0)
3439 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3440 errmsg("column \"%s\" has a compression method conflict",
3442 errdetail("%s versus %s",
3443 prevdef
->compression
, newdef
->compression
)));
3447 * Check for GENERATED conflicts
3449 if (prevdef
->generated
!= newdef
->generated
)
3451 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3452 errmsg("inherited column \"%s\" has a generation conflict",
3456 * Default and other constraints are handled by the caller.
3459 prevdef
->inhcount
++;
3460 if (prevdef
->inhcount
< 0)
3462 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3463 errmsg("too many inheritance parents"));
3469 * StoreCatalogInheritance
3470 * Updates the system catalogs with proper inheritance information.
3472 * supers is a list of the OIDs of the new relation's direct ancestors.
3475 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3476 bool child_is_partition
)
3485 Assert(OidIsValid(relationId
));
3491 * Store INHERITS information in pg_inherits using direct ancestors only.
3492 * Also enter dependencies on the direct ancestors, and make sure they are
3493 * marked with relhassubclass = true.
3495 * (Once upon a time, both direct and indirect ancestors were found here
3496 * and then entered into pg_ipl. Since that catalog doesn't exist
3497 * anymore, there's no need to look for indirect ancestors.)
3499 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3502 foreach(entry
, supers
)
3504 Oid parentOid
= lfirst_oid(entry
);
3506 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3507 child_is_partition
);
3511 table_close(relation
, RowExclusiveLock
);
3515 * Make catalog entries showing relationId as being an inheritance child
3516 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3519 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3520 int32 seqNumber
, Relation inhRelation
,
3521 bool child_is_partition
)
3523 ObjectAddress childobject
,
3526 /* store the pg_inherits row */
3527 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3530 * Store a dependency too
3532 parentobject
.classId
= RelationRelationId
;
3533 parentobject
.objectId
= parentOid
;
3534 parentobject
.objectSubId
= 0;
3535 childobject
.classId
= RelationRelationId
;
3536 childobject
.objectId
= relationId
;
3537 childobject
.objectSubId
= 0;
3539 recordDependencyOn(&childobject
, &parentobject
,
3540 child_dependency_type(child_is_partition
));
3543 * Post creation hook of this inheritance. Since object_access_hook
3544 * doesn't take multiple object identifiers, we relay oid of parent
3545 * relation using auxiliary_id argument.
3547 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3552 * Mark the parent as having subclasses.
3554 SetRelationHasSubclass(parentOid
, true);
3558 * Look for an existing column entry with the given name.
3560 * Returns the index (starting with 1) if attribute already exists in columns,
3564 findAttrByName(const char *attributeName
, const List
*columns
)
3569 foreach(lc
, columns
)
3571 if (strcmp(attributeName
, lfirst_node(ColumnDef
, lc
)->colname
) == 0)
3581 * SetRelationHasSubclass
3582 * Set the value of the relation's relhassubclass field in pg_class.
3584 * NOTE: caller must be holding an appropriate lock on the relation.
3585 * ShareUpdateExclusiveLock is sufficient.
3587 * NOTE: an important side-effect of this operation is that an SI invalidation
3588 * message is sent out to all backends --- including me --- causing plans
3589 * referencing the relation to be rebuilt with the new list of children.
3590 * This must happen even if we find that no change is needed in the pg_class
3594 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3596 Relation relationRelation
;
3598 Form_pg_class classtuple
;
3601 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3603 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3604 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3605 if (!HeapTupleIsValid(tuple
))
3606 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3607 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3609 if (classtuple
->relhassubclass
!= relhassubclass
)
3611 classtuple
->relhassubclass
= relhassubclass
;
3612 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3616 /* no need to change tuple, but force relcache rebuild anyway */
3617 CacheInvalidateRelcacheByTuple(tuple
);
3620 heap_freetuple(tuple
);
3621 table_close(relationRelation
, RowExclusiveLock
);
3625 * CheckRelationTableSpaceMove
3626 * Check if relation can be moved to new tablespace.
3628 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3630 * Returns true if the relation can be moved to the new tablespace; raises
3631 * an error if it is not possible to do the move; returns false if the move
3632 * would have no effect.
3635 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3637 Oid oldTableSpaceId
;
3640 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3643 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3644 if (newTableSpaceId
== oldTableSpaceId
||
3645 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3649 * We cannot support moving mapped relations into different tablespaces.
3650 * (In particular this eliminates all shared catalogs.)
3652 if (RelationIsMapped(rel
))
3654 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3655 errmsg("cannot move system relation \"%s\"",
3656 RelationGetRelationName(rel
))));
3658 /* Cannot move a non-shared relation into pg_global */
3659 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3661 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3662 errmsg("only shared relations can be placed in pg_global tablespace")));
3665 * Do not allow moving temp tables of other backends ... their local
3666 * buffer manager is not going to cope.
3668 if (RELATION_IS_OTHER_TEMP(rel
))
3670 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3671 errmsg("cannot move temporary tables of other sessions")));
3677 * SetRelationTableSpace
3678 * Set new reltablespace and relfilenumber in pg_class entry.
3680 * newTableSpaceId is the new tablespace for the relation, and
3681 * newRelFilenumber its new filenumber. If newRelFilenumber is
3682 * InvalidRelFileNumber, this field is not updated.
3684 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3686 * The caller of this routine had better check if a relation can be
3687 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3688 * first, and is responsible for making the change visible with
3689 * CommandCounterIncrement().
3692 SetRelationTableSpace(Relation rel
,
3693 Oid newTableSpaceId
,
3694 RelFileNumber newRelFilenumber
)
3698 Form_pg_class rd_rel
;
3699 Oid reloid
= RelationGetRelid(rel
);
3701 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3703 /* Get a modifiable copy of the relation's pg_class row. */
3704 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3706 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3707 if (!HeapTupleIsValid(tuple
))
3708 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3709 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3711 /* Update the pg_class row. */
3712 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3713 InvalidOid
: newTableSpaceId
;
3714 if (RelFileNumberIsValid(newRelFilenumber
))
3715 rd_rel
->relfilenode
= newRelFilenumber
;
3716 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
3719 * Record dependency on tablespace. This is only required for relations
3720 * that have no physical storage.
3722 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3723 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3724 rd_rel
->reltablespace
);
3726 heap_freetuple(tuple
);
3727 table_close(pg_class
, RowExclusiveLock
);
3731 * renameatt_check - basic sanity checks before attribute rename
3734 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3736 char relkind
= classform
->relkind
;
3738 if (classform
->reloftype
&& !recursing
)
3740 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3741 errmsg("cannot rename column of typed table")));
3744 * Renaming the columns of sequences or toast tables doesn't actually
3745 * break anything from the system's point of view, since internal
3746 * references are by attnum. But it doesn't seem right to allow users to
3747 * change names that are hardcoded into the system, hence the following
3750 if (relkind
!= RELKIND_RELATION
&&
3751 relkind
!= RELKIND_VIEW
&&
3752 relkind
!= RELKIND_MATVIEW
&&
3753 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3754 relkind
!= RELKIND_INDEX
&&
3755 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3756 relkind
!= RELKIND_FOREIGN_TABLE
&&
3757 relkind
!= RELKIND_PARTITIONED_TABLE
)
3759 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3760 errmsg("cannot rename columns of relation \"%s\"",
3761 NameStr(classform
->relname
)),
3762 errdetail_relkind_not_supported(relkind
)));
3765 * permissions checking. only the owner of a class can change its schema.
3767 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3768 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3769 NameStr(classform
->relname
));
3770 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3772 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3773 errmsg("permission denied: \"%s\" is a system catalog",
3774 NameStr(classform
->relname
))));
3778 * renameatt_internal - workhorse for renameatt
3780 * Return value is the attribute number in the 'myrelid' relation.
3783 renameatt_internal(Oid myrelid
,
3784 const char *oldattname
,
3785 const char *newattname
,
3788 int expected_parents
,
3789 DropBehavior behavior
)
3791 Relation targetrelation
;
3792 Relation attrelation
;
3794 Form_pg_attribute attform
;
3798 * Grab an exclusive lock on the target table, which we will NOT release
3799 * until end of transaction.
3801 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3802 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3805 * if the 'recurse' flag is set then we are supposed to rename this
3806 * attribute in all classes that inherit from 'relname' (as well as in
3809 * any permissions or problems with duplicate attributes will cause the
3810 * whole transaction to abort, which is what we want -- all or nothing.
3820 * we need the number of parents for each child so that the recursive
3821 * calls to renameatt() can determine whether there are any parents
3822 * outside the inheritance hierarchy being processed.
3824 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3828 * find_all_inheritors does the recursive search of the inheritance
3829 * hierarchy, so all we have to do is process all of the relids in the
3830 * list that it returns.
3832 forboth(lo
, child_oids
, li
, child_numparents
)
3834 Oid childrelid
= lfirst_oid(lo
);
3835 int numparents
= lfirst_int(li
);
3837 if (childrelid
== myrelid
)
3839 /* note we need not recurse again */
3840 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3846 * If we are told not to recurse, there had better not be any child
3847 * tables; else the rename would put them out of step.
3849 * expected_parents will only be 0 if we are not already recursing.
3851 if (expected_parents
== 0 &&
3852 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3854 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3855 errmsg("inherited column \"%s\" must be renamed in child tables too",
3859 /* rename attributes in typed tables of composite type */
3860 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3865 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3866 RelationGetRelationName(targetrelation
),
3869 foreach(lo
, child_oids
)
3870 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3873 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3875 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3876 if (!HeapTupleIsValid(atttup
))
3878 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3879 errmsg("column \"%s\" does not exist",
3881 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3883 attnum
= attform
->attnum
;
3886 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3887 errmsg("cannot rename system column \"%s\"",
3891 * if the attribute is inherited, forbid the renaming. if this is a
3892 * top-level call to renameatt(), then expected_parents will be 0, so the
3893 * effect of this code will be to prohibit the renaming if the attribute
3894 * is inherited at all. if this is a recursive call to renameatt(),
3895 * expected_parents will be the number of parents the current relation has
3896 * within the inheritance hierarchy being processed, so we'll prohibit the
3897 * renaming only if there are additional parents from elsewhere.
3899 if (attform
->attinhcount
> expected_parents
)
3901 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3902 errmsg("cannot rename inherited column \"%s\"",
3905 /* new name should not already exist */
3906 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3908 /* apply the update */
3909 namestrcpy(&(attform
->attname
), newattname
);
3911 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3913 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3915 heap_freetuple(atttup
);
3917 table_close(attrelation
, RowExclusiveLock
);
3919 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3925 * Perform permissions and integrity checks before acquiring a relation lock.
3928 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3934 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3935 if (!HeapTupleIsValid(tuple
))
3936 return; /* concurrently dropped */
3937 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3938 renameatt_check(relid
, form
, false);
3939 ReleaseSysCache(tuple
);
3943 * renameatt - changes the name of an attribute in a relation
3945 * The returned ObjectAddress is that of the renamed column.
3948 renameatt(RenameStmt
*stmt
)
3952 ObjectAddress address
;
3954 /* lock level taken here should match renameatt_internal */
3955 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3956 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3957 RangeVarCallbackForRenameAttribute
,
3960 if (!OidIsValid(relid
))
3963 (errmsg("relation \"%s\" does not exist, skipping",
3964 stmt
->relation
->relname
)));
3965 return InvalidObjectAddress
;
3969 renameatt_internal(relid
,
3970 stmt
->subname
, /* old att name */
3971 stmt
->newname
, /* new att name */
3972 stmt
->relation
->inh
, /* recursive? */
3973 false, /* recursing? */
3974 0, /* expected inhcount */
3977 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3983 * same logic as renameatt_internal
3985 static ObjectAddress
3986 rename_constraint_internal(Oid myrelid
,
3988 const char *oldconname
,
3989 const char *newconname
,
3992 int expected_parents
)
3994 Relation targetrelation
= NULL
;
3997 Form_pg_constraint con
;
3998 ObjectAddress address
;
4000 Assert(!myrelid
|| !mytypid
);
4004 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
4008 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
4011 * don't tell it whether we're recursing; we allow changing typed
4014 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
4016 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
4019 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
4020 if (!HeapTupleIsValid(tuple
))
4021 elog(ERROR
, "cache lookup failed for constraint %u",
4023 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
4026 (con
->contype
== CONSTRAINT_CHECK
||
4027 con
->contype
== CONSTRAINT_NOTNULL
) &&
4037 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
4040 forboth(lo
, child_oids
, li
, child_numparents
)
4042 Oid childrelid
= lfirst_oid(lo
);
4043 int numparents
= lfirst_int(li
);
4045 if (childrelid
== myrelid
)
4048 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
4053 if (expected_parents
== 0 &&
4054 find_inheritance_children(myrelid
, NoLock
) != NIL
)
4056 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4057 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4061 if (con
->coninhcount
> expected_parents
)
4063 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4064 errmsg("cannot rename inherited constraint \"%s\"",
4069 && (con
->contype
== CONSTRAINT_PRIMARY
4070 || con
->contype
== CONSTRAINT_UNIQUE
4071 || con
->contype
== CONSTRAINT_EXCLUSION
))
4072 /* rename the index; this renames the constraint as well */
4073 RenameRelationInternal(con
->conindid
, newconname
, false, true);
4075 RenameConstraintById(constraintOid
, newconname
);
4077 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
4079 ReleaseSysCache(tuple
);
4084 * Invalidate relcache so as others can see the new constraint name.
4086 CacheInvalidateRelcache(targetrelation
);
4088 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
4095 RenameConstraint(RenameStmt
*stmt
)
4097 Oid relid
= InvalidOid
;
4098 Oid typid
= InvalidOid
;
4100 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
4105 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
4106 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
4107 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
4108 if (!HeapTupleIsValid(tup
))
4109 elog(ERROR
, "cache lookup failed for type %u", typid
);
4110 checkDomainOwner(tup
);
4111 ReleaseSysCache(tup
);
4112 table_close(rel
, NoLock
);
4116 /* lock level taken here should match rename_constraint_internal */
4117 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
4118 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4119 RangeVarCallbackForRenameAttribute
,
4121 if (!OidIsValid(relid
))
4124 (errmsg("relation \"%s\" does not exist, skipping",
4125 stmt
->relation
->relname
)));
4126 return InvalidObjectAddress
;
4131 rename_constraint_internal(relid
, typid
,
4135 stmt
->relation
->inh
), /* recursive? */
4136 false, /* recursing? */
4137 0 /* expected inhcount */ );
4141 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4145 RenameRelation(RenameStmt
*stmt
)
4147 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
4149 ObjectAddress address
;
4152 * Grab an exclusive lock on the target table, index, sequence, view,
4153 * materialized view, or foreign table, which we will NOT release until
4154 * end of transaction.
4156 * Lock level used here should match RenameRelationInternal, to avoid lock
4157 * escalation. However, because ALTER INDEX can be used with any relation
4158 * type, we mustn't believe without verification.
4166 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
4168 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4169 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4170 RangeVarCallbackForAlterRelation
,
4173 if (!OidIsValid(relid
))
4176 (errmsg("relation \"%s\" does not exist, skipping",
4177 stmt
->relation
->relname
)));
4178 return InvalidObjectAddress
;
4182 * We allow mismatched statement and object types (e.g., ALTER INDEX
4183 * to rename a table), but we might've used the wrong lock level. If
4184 * that happens, retry with the correct lock level. We don't bother
4185 * if we already acquired AccessExclusiveLock with an index, however.
4187 relkind
= get_rel_relkind(relid
);
4188 obj_is_index
= (relkind
== RELKIND_INDEX
||
4189 relkind
== RELKIND_PARTITIONED_INDEX
);
4190 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
4193 UnlockRelationOid(relid
, lockmode
);
4194 is_index_stmt
= obj_is_index
;
4198 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
4200 ObjectAddressSet(address
, RelationRelationId
, relid
);
4206 * RenameRelationInternal - change the name of a relation
4209 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
4211 Relation targetrelation
;
4212 Relation relrelation
; /* for RELATION relation */
4214 Form_pg_class relform
;
4218 * Grab a lock on the target relation, which we will NOT release until end
4219 * of transaction. We need at least a self-exclusive lock so that
4220 * concurrent DDL doesn't overwrite the rename if they start updating
4221 * while still seeing the old version. The lock also guards against
4222 * triggering relcache reloads in concurrent sessions, which might not
4223 * handle this information changing under them. For indexes, we can use a
4224 * reduced lock level because RelationReloadIndexInfo() handles indexes
4227 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
4228 namespaceId
= RelationGetNamespace(targetrelation
);
4231 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4233 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4235 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4236 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4237 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4238 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4240 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
4242 (errcode(ERRCODE_DUPLICATE_TABLE
),
4243 errmsg("relation \"%s\" already exists",
4247 * RenameRelation is careful not to believe the caller's idea of the
4248 * relation kind being handled. We don't have to worry about this, but
4249 * let's not be totally oblivious to it. We can process an index as
4250 * not-an-index, but not the other way around.
4253 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4254 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4257 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4258 * because it's a copy...)
4260 namestrcpy(&(relform
->relname
), newrelname
);
4262 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4264 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4265 InvalidOid
, is_internal
);
4267 heap_freetuple(reltup
);
4268 table_close(relrelation
, RowExclusiveLock
);
4271 * Also rename the associated type, if any.
4273 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4274 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4275 newrelname
, namespaceId
);
4278 * Also rename the associated constraint, if any.
4280 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4281 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4283 Oid constraintId
= get_index_constraint(myrelid
);
4285 if (OidIsValid(constraintId
))
4286 RenameConstraintById(constraintId
, newrelname
);
4290 * Close rel, but keep lock!
4292 relation_close(targetrelation
, NoLock
);
4296 * ResetRelRewrite - reset relrewrite
4299 ResetRelRewrite(Oid myrelid
)
4301 Relation relrelation
; /* for RELATION relation */
4303 Form_pg_class relform
;
4306 * Find relation's pg_class tuple.
4308 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4310 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4311 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4312 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4313 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4316 * Update pg_class tuple.
4318 relform
->relrewrite
= InvalidOid
;
4320 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4322 heap_freetuple(reltup
);
4323 table_close(relrelation
, RowExclusiveLock
);
4327 * Disallow ALTER TABLE (and similar commands) when the current backend has
4328 * any open reference to the target table besides the one just acquired by
4329 * the calling command; this implies there's an open cursor or active plan.
4330 * We need this check because our lock doesn't protect us against stomping
4331 * on our own foot, only other people's feet!
4333 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4334 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4335 * possibly be relaxed to only error out for certain types of alterations.
4336 * But the use-case for allowing any of these things is not obvious, so we
4337 * won't work hard at it for now.
4339 * We also reject these commands if there are any pending AFTER trigger events
4340 * for the rel. This is certainly necessary for the rewriting variants of
4341 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4342 * events would try to fetch the wrong tuples. It might be overly cautious
4343 * in other cases, but again it seems better to err on the side of paranoia.
4345 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4346 * we are worried about active indexscans on the index. The trigger-event
4347 * check can be skipped, since we are doing no damage to the parent table.
4349 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4352 CheckTableNotInUse(Relation rel
, const char *stmt
)
4354 int expected_refcnt
;
4356 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4357 if (rel
->rd_refcnt
!= expected_refcnt
)
4359 (errcode(ERRCODE_OBJECT_IN_USE
),
4360 /* translator: first %s is a SQL command, eg ALTER TABLE */
4361 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4362 stmt
, RelationGetRelationName(rel
))));
4364 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4365 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4366 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4368 (errcode(ERRCODE_OBJECT_IN_USE
),
4369 /* translator: first %s is a SQL command, eg ALTER TABLE */
4370 errmsg("cannot %s \"%s\" because it has pending trigger events",
4371 stmt
, RelationGetRelationName(rel
))));
4375 * AlterTableLookupRelation
4376 * Look up, and lock, the OID for the relation named by an alter table
4380 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4382 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4383 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4384 RangeVarCallbackForAlterRelation
,
4390 * Execute ALTER TABLE, which can be a list of subcommands
4392 * ALTER TABLE is performed in three phases:
4393 * 1. Examine subcommands and perform pre-transformation checking.
4394 * 2. Validate and transform subcommands, and update system catalogs.
4395 * 3. Scan table(s) to check new constraints, and optionally recopy
4396 * the data into new table(s).
4397 * Phase 3 is not performed unless one or more of the subcommands requires
4398 * it. The intention of this design is to allow multiple independent
4399 * updates of the table schema to be performed with only one pass over the
4402 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4403 * each table to be affected (there may be multiple affected tables if the
4404 * commands traverse a table inheritance hierarchy). Also we do preliminary
4405 * validation of the subcommands. Because earlier subcommands may change
4406 * the catalog state seen by later commands, there are limits to what can
4407 * be done in this phase. Generally, this phase acquires table locks,
4408 * checks permissions and relkind, and recurses to find child tables.
4410 * ATRewriteCatalogs performs phase 2 for each affected table.
4411 * Certain subcommands need to be performed before others to avoid
4412 * unnecessary conflicts; for example, DROP COLUMN should come before
4413 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4414 * lists, one for each logical "pass" of phase 2.
4416 * ATRewriteTables performs phase 3 for those tables that need it.
4418 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4419 * since phase 1 already does it. However, for certain subcommand types
4420 * it is only possible to determine how to recurse at phase 2 time; for
4421 * those cases, phase 1 sets the cmd->recurse flag.
4423 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4424 * the whole operation; we don't have to do anything special to clean up.
4426 * The caller must lock the relation, with an appropriate lock level
4427 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4428 * or higher. We pass the lock level down
4429 * so that we can apply it recursively to inherited tables. Note that the
4430 * lock level we want as we recurse might well be higher than required for
4431 * that specific subcommand. So we pass down the overall lock requirement,
4432 * rather than reassess it at lower levels.
4434 * The caller also provides a "context" which is to be passed back to
4435 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4436 * Some of the fields therein, such as the relid, are used here as well.
4439 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4440 AlterTableUtilityContext
*context
)
4444 /* Caller is required to provide an adequate lock. */
4445 rel
= relation_open(context
->relid
, NoLock
);
4447 CheckTableNotInUse(rel
, "ALTER TABLE");
4449 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4453 * AlterTableInternal
4455 * ALTER TABLE with target specified by OID
4457 * We do not reject if the relation is already open, because it's quite
4458 * likely that one or more layers of caller have it open. That means it
4459 * is unsafe to use this entry point for alterations that could break
4460 * existing query plans. On the assumption it's not used for such, we
4461 * don't have to reject pending AFTER triggers, either.
4463 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4464 * used for any subcommand types that require parse transformation or
4465 * could generate subcommands that have to be passed to ProcessUtility.
4468 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4471 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4473 rel
= relation_open(relid
, lockmode
);
4475 EventTriggerAlterTableRelid(relid
);
4477 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4481 * AlterTableGetLockLevel
4483 * Sets the overall lock level required for the supplied list of subcommands.
4484 * Policy for doing this set according to needs of AlterTable(), see
4485 * comments there for overall explanation.
4487 * Function is called before and after parsing, so it must give same
4488 * answer each time it is called. Some subcommands are transformed
4489 * into other subcommand types, so the transform must never be made to a
4490 * lower lock level than previously assigned. All transforms are noted below.
4492 * Since this is called before we lock the table we cannot use table metadata
4493 * to influence the type of lock we acquire.
4495 * There should be no lockmodes hardcoded into the subcommand functions. All
4496 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4497 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4498 * and does not travel through this section of code and cannot be combined with
4499 * any of the subcommands given here.
4501 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4502 * so any changes that might affect SELECTs running on standbys need to use
4503 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4504 * have a solution for that also.
4506 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4507 * that takes a lock less than AccessExclusiveLock can change object definitions
4508 * while pg_dump is running. Be careful to check that the appropriate data is
4509 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4510 * otherwise we might end up with an inconsistent dump that can't restore.
4513 AlterTableGetLockLevel(List
*cmds
)
4516 * This only works if we read catalog tables using MVCC snapshots.
4519 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4523 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4524 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4526 switch (cmd
->subtype
)
4529 * These subcommands rewrite the heap, so require full locks.
4531 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4533 case AT_SetAccessMethod
: /* must rewrite heap */
4534 case AT_SetTableSpace
: /* must rewrite heap */
4535 case AT_AlterColumnType
: /* must rewrite heap */
4536 cmd_lockmode
= AccessExclusiveLock
;
4540 * These subcommands may require addition of toast tables. If
4541 * we add a toast table to a table currently being scanned, we
4542 * might miss data added to the new toast table by concurrent
4543 * insert transactions.
4545 case AT_SetStorage
: /* may add toast tables, see
4546 * ATRewriteCatalogs() */
4547 cmd_lockmode
= AccessExclusiveLock
;
4551 * Removing constraints can affect SELECTs that have been
4552 * optimized assuming the constraint holds true. See also
4553 * CloneFkReferenced.
4555 case AT_DropConstraint
: /* as DROP INDEX */
4556 case AT_DropNotNull
: /* may change some SQL plans */
4557 cmd_lockmode
= AccessExclusiveLock
;
4561 * Subcommands that may be visible to concurrent SELECTs
4563 case AT_DropColumn
: /* change visible to SELECT */
4564 case AT_AddColumnToView
: /* CREATE VIEW */
4565 case AT_DropOids
: /* used to equiv to DropColumn */
4566 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4567 case AT_EnableReplicaRule
: /* may change SELECT rules */
4568 case AT_EnableRule
: /* may change SELECT rules */
4569 case AT_DisableRule
: /* may change SELECT rules */
4570 cmd_lockmode
= AccessExclusiveLock
;
4574 * Changing owner may remove implicit SELECT privileges
4576 case AT_ChangeOwner
: /* change visible to SELECT */
4577 cmd_lockmode
= AccessExclusiveLock
;
4581 * Changing foreign table options may affect optimization.
4583 case AT_GenericOptions
:
4584 case AT_AlterColumnGenericOptions
:
4585 cmd_lockmode
= AccessExclusiveLock
;
4589 * These subcommands affect write operations only.
4592 case AT_EnableAlwaysTrig
:
4593 case AT_EnableReplicaTrig
:
4594 case AT_EnableTrigAll
:
4595 case AT_EnableTrigUser
:
4596 case AT_DisableTrig
:
4597 case AT_DisableTrigAll
:
4598 case AT_DisableTrigUser
:
4599 cmd_lockmode
= ShareRowExclusiveLock
;
4603 * These subcommands affect write operations only. XXX
4604 * Theoretically, these could be ShareRowExclusiveLock.
4606 case AT_ColumnDefault
:
4607 case AT_CookedColumnDefault
:
4608 case AT_AlterConstraint
:
4609 case AT_AddIndex
: /* from ADD CONSTRAINT */
4610 case AT_AddIndexConstraint
:
4611 case AT_ReplicaIdentity
:
4613 case AT_SetAttNotNull
:
4614 case AT_EnableRowSecurity
:
4615 case AT_DisableRowSecurity
:
4616 case AT_ForceRowSecurity
:
4617 case AT_NoForceRowSecurity
:
4618 case AT_AddIdentity
:
4619 case AT_DropIdentity
:
4620 case AT_SetIdentity
:
4621 case AT_SetExpression
:
4622 case AT_DropExpression
:
4623 case AT_SetCompression
:
4624 cmd_lockmode
= AccessExclusiveLock
;
4627 case AT_AddConstraint
:
4628 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4629 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4630 if (IsA(cmd
->def
, Constraint
))
4632 Constraint
*con
= (Constraint
*) cmd
->def
;
4634 switch (con
->contype
)
4636 case CONSTR_EXCLUSION
:
4637 case CONSTR_PRIMARY
:
4641 * Cases essentially the same as CREATE INDEX. We
4642 * could reduce the lock strength to ShareLock if
4643 * we can work out how to allow concurrent catalog
4644 * updates. XXX Might be set down to
4645 * ShareRowExclusiveLock but requires further
4648 cmd_lockmode
= AccessExclusiveLock
;
4650 case CONSTR_FOREIGN
:
4653 * We add triggers to both tables when we add a
4654 * Foreign Key, so the lock level must be at least
4655 * as strong as CREATE TRIGGER.
4657 cmd_lockmode
= ShareRowExclusiveLock
;
4661 cmd_lockmode
= AccessExclusiveLock
;
4667 * These subcommands affect inheritance behaviour. Queries
4668 * started before us will continue to see the old inheritance
4669 * behaviour, while queries started after we commit will see
4670 * new behaviour. No need to prevent reads or writes to the
4671 * subtable while we hook it up though. Changing the TupDesc
4672 * may be a problem, so keep highest lock.
4675 case AT_DropInherit
:
4676 cmd_lockmode
= AccessExclusiveLock
;
4680 * These subcommands affect implicit row type conversion. They
4681 * have affects similar to CREATE/DROP CAST on queries. don't
4682 * provide for invalidating parse trees as a result of such
4683 * changes, so we keep these at AccessExclusiveLock.
4687 cmd_lockmode
= AccessExclusiveLock
;
4691 * Only used by CREATE OR REPLACE VIEW which must conflict
4692 * with an SELECTs currently using the view.
4694 case AT_ReplaceRelOptions
:
4695 cmd_lockmode
= AccessExclusiveLock
;
4699 * These subcommands affect general strategies for performance
4700 * and maintenance, though don't change the semantic results
4701 * from normal data reads and writes. Delaying an ALTER TABLE
4702 * behind currently active writes only delays the point where
4703 * the new strategy begins to take effect, so there is no
4704 * benefit in waiting. In this case the minimum restriction
4705 * applies: we don't currently allow concurrent catalog
4708 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4709 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4710 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4711 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4712 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4713 cmd_lockmode
= ShareUpdateExclusiveLock
;
4717 case AT_SetUnLogged
:
4718 cmd_lockmode
= AccessExclusiveLock
;
4721 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4722 cmd_lockmode
= ShareUpdateExclusiveLock
;
4726 * Rel options are more complex than first appears. Options
4727 * are set here for tables, views and indexes; for historical
4728 * reasons these can all be used with ALTER TABLE, so we can't
4729 * decide between them using the basic grammar.
4731 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4733 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4735 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4738 case AT_AttachPartition
:
4739 cmd_lockmode
= ShareUpdateExclusiveLock
;
4742 case AT_DetachPartition
:
4743 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4744 cmd_lockmode
= ShareUpdateExclusiveLock
;
4746 cmd_lockmode
= AccessExclusiveLock
;
4749 case AT_DetachPartitionFinalize
:
4750 cmd_lockmode
= ShareUpdateExclusiveLock
;
4753 case AT_SplitPartition
:
4754 cmd_lockmode
= AccessExclusiveLock
;
4757 case AT_MergePartitions
:
4758 cmd_lockmode
= AccessExclusiveLock
;
4762 elog(ERROR
, "unrecognized alter table type: %d",
4763 (int) cmd
->subtype
);
4768 * Take the greatest lockmode from any subcommand
4770 if (cmd_lockmode
> lockmode
)
4771 lockmode
= cmd_lockmode
;
4778 * ATController provides top level control over the phases.
4780 * parsetree is passed in to allow it to be passed to event triggers
4784 ATController(AlterTableStmt
*parsetree
,
4785 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4786 AlterTableUtilityContext
*context
)
4791 /* Phase 1: preliminary examination of commands, create work queue */
4794 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4796 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4799 /* Close the relation, but keep lock until commit */
4800 relation_close(rel
, NoLock
);
4802 /* Phase 2: update system catalogs */
4803 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4805 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4806 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4812 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4813 * recursion and permission checks.
4815 * Caller must have acquired appropriate lock type on relation already.
4816 * This lock should be held until commit.
4819 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4820 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4821 AlterTableUtilityContext
*context
)
4823 AlteredTableInfo
*tab
;
4824 AlterTablePass pass
= AT_PASS_UNSET
;
4826 /* Find or create work queue entry for this table */
4827 tab
= ATGetQueueEntry(wqueue
, rel
);
4830 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4831 * partitions that are pending detach.
4833 if (rel
->rd_rel
->relispartition
&&
4834 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4835 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4837 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4838 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4839 RelationGetRelationName(rel
)),
4840 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4843 * Copy the original subcommand for each table, so we can scribble on it.
4844 * This avoids conflicts when different child tables need to make
4845 * different parse transformations (for example, the same column may have
4846 * different column numbers in different children).
4848 cmd
= copyObject(cmd
);
4851 * Do permissions and relkind checking, recursion to child tables if
4852 * needed, and any additional phase-1 processing needed. (But beware of
4853 * adding any processing that looks at table details that another
4854 * subcommand could change. In some cases we reject multiple subcommands
4855 * that could try to change the same state in contrary ways.)
4857 switch (cmd
->subtype
)
4859 case AT_AddColumn
: /* ADD COLUMN */
4860 ATSimplePermissions(cmd
->subtype
, rel
,
4861 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4862 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4864 /* Recursion occurs during execution phase */
4865 pass
= AT_PASS_ADD_COL
;
4867 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4868 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4869 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4871 /* Recursion occurs during execution phase */
4872 pass
= AT_PASS_ADD_COL
;
4874 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4877 * We allow defaults on views so that INSERT into a view can have
4878 * default-ish behavior. This works because the rewriter
4879 * substitutes default values into INSERTs before it expands
4882 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4883 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4884 /* No command-specific prep needed */
4885 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4887 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4888 /* This is currently used only in CREATE TABLE */
4889 /* (so the permission check really isn't necessary) */
4890 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4891 /* This command never recurses */
4892 pass
= AT_PASS_ADD_OTHERCONSTR
;
4894 case AT_AddIdentity
:
4895 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4896 /* Set up recursion for phase 2; no other prep needed */
4898 cmd
->recurse
= true;
4899 pass
= AT_PASS_ADD_OTHERCONSTR
;
4901 case AT_SetIdentity
:
4902 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4903 /* Set up recursion for phase 2; no other prep needed */
4905 cmd
->recurse
= true;
4906 /* This should run after AddIdentity, so do it in MISC pass */
4907 pass
= AT_PASS_MISC
;
4909 case AT_DropIdentity
:
4910 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4911 /* Set up recursion for phase 2; no other prep needed */
4913 cmd
->recurse
= true;
4914 pass
= AT_PASS_DROP
;
4916 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4917 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4918 /* Set up recursion for phase 2; no other prep needed */
4920 cmd
->recurse
= true;
4921 pass
= AT_PASS_DROP
;
4923 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4924 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4925 /* Set up recursion for phase 2; no other prep needed */
4927 cmd
->recurse
= true;
4928 pass
= AT_PASS_COL_ATTRS
;
4930 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull without adding
4932 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4933 /* Need command-specific recursion decision */
4934 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4935 pass
= AT_PASS_COL_ATTRS
;
4937 case AT_SetExpression
: /* ALTER COLUMN SET EXPRESSION */
4938 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4939 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4940 pass
= AT_PASS_SET_EXPRESSION
;
4942 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4943 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4944 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4945 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4946 pass
= AT_PASS_DROP
;
4948 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4949 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4950 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4951 /* No command-specific prep needed */
4952 pass
= AT_PASS_MISC
;
4954 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4955 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4956 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4957 /* This command never recurses */
4958 pass
= AT_PASS_MISC
;
4960 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4961 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4962 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4963 /* No command-specific prep needed */
4964 pass
= AT_PASS_MISC
;
4966 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4967 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4968 /* This command never recurses */
4969 /* No command-specific prep needed */
4970 pass
= AT_PASS_MISC
;
4972 case AT_DropColumn
: /* DROP COLUMN */
4973 ATSimplePermissions(cmd
->subtype
, rel
,
4974 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4975 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
4977 /* Recursion occurs during execution phase */
4978 pass
= AT_PASS_DROP
;
4980 case AT_AddIndex
: /* ADD INDEX */
4981 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4982 /* This command never recurses */
4983 /* No command-specific prep needed */
4984 pass
= AT_PASS_ADD_INDEX
;
4986 case AT_AddConstraint
: /* ADD CONSTRAINT */
4987 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4990 /* recurses at exec time; lock descendants and set flag */
4991 (void) find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
4992 cmd
->recurse
= true;
4994 pass
= AT_PASS_ADD_CONSTR
;
4996 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
4997 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4998 /* This command never recurses */
4999 /* No command-specific prep needed */
5000 pass
= AT_PASS_ADD_INDEXCONSTR
;
5002 case AT_DropConstraint
: /* DROP CONSTRAINT */
5003 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5004 ATCheckPartitionsNotInUse(rel
, lockmode
);
5005 /* Other recursion occurs during execution phase */
5006 /* No command-specific prep needed except saving recurse flag */
5008 cmd
->recurse
= true;
5009 pass
= AT_PASS_DROP
;
5011 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5012 ATSimplePermissions(cmd
->subtype
, rel
,
5013 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
5014 /* See comments for ATPrepAlterColumnType */
5015 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
5016 AT_PASS_UNSET
, context
);
5017 Assert(cmd
!= NULL
);
5018 /* Performs own recursion */
5019 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
5021 pass
= AT_PASS_ALTER_TYPE
;
5023 case AT_AlterColumnGenericOptions
:
5024 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5025 /* This command never recurses */
5026 /* No command-specific prep needed */
5027 pass
= AT_PASS_MISC
;
5029 case AT_ChangeOwner
: /* ALTER OWNER */
5030 /* This command never recurses */
5031 /* No command-specific prep needed */
5032 pass
= AT_PASS_MISC
;
5034 case AT_ClusterOn
: /* CLUSTER ON */
5035 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5036 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5037 /* These commands never recurse */
5038 /* No command-specific prep needed */
5039 pass
= AT_PASS_MISC
;
5041 case AT_SetLogged
: /* SET LOGGED */
5042 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
5043 if (tab
->chgPersistence
)
5045 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5046 errmsg("cannot change persistence setting twice")));
5047 tab
->chgPersistence
= ATPrepChangePersistence(rel
, true);
5048 /* force rewrite if necessary; see comment in ATRewriteTables */
5049 if (tab
->chgPersistence
)
5051 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
5052 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
5054 pass
= AT_PASS_MISC
;
5056 case AT_SetUnLogged
: /* SET UNLOGGED */
5057 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
5058 if (tab
->chgPersistence
)
5060 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5061 errmsg("cannot change persistence setting twice")));
5062 tab
->chgPersistence
= ATPrepChangePersistence(rel
, false);
5063 /* force rewrite if necessary; see comment in ATRewriteTables */
5064 if (tab
->chgPersistence
)
5066 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
5067 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
5069 pass
= AT_PASS_MISC
;
5071 case AT_DropOids
: /* SET WITHOUT OIDS */
5072 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5073 pass
= AT_PASS_DROP
;
5075 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5076 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5078 /* check if another access method change was already requested */
5079 if (tab
->chgAccessMethod
)
5081 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5082 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5084 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
5085 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
5087 case AT_SetTableSpace
: /* SET TABLESPACE */
5088 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
|
5089 ATT_PARTITIONED_INDEX
);
5090 /* This command never recurses */
5091 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
5092 pass
= AT_PASS_MISC
; /* doesn't actually matter */
5094 case AT_SetRelOptions
: /* SET (...) */
5095 case AT_ResetRelOptions
: /* RESET (...) */
5096 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
5097 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_MATVIEW
| ATT_INDEX
);
5098 /* This command never recurses */
5099 /* No command-specific prep needed */
5100 pass
= AT_PASS_MISC
;
5102 case AT_AddInherit
: /* INHERIT */
5103 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5104 /* This command never recurses */
5105 ATPrepAddInherit(rel
);
5106 pass
= AT_PASS_MISC
;
5108 case AT_DropInherit
: /* NO INHERIT */
5109 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5110 /* This command never recurses */
5111 /* No command-specific prep needed */
5112 pass
= AT_PASS_MISC
;
5114 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5115 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5116 /* Recursion occurs during execution phase */
5117 pass
= AT_PASS_MISC
;
5119 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5120 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5121 /* Recursion occurs during execution phase */
5122 /* No command-specific prep needed except saving recurse flag */
5124 cmd
->recurse
= true;
5125 pass
= AT_PASS_MISC
;
5127 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
5128 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5129 pass
= AT_PASS_MISC
;
5130 /* This command never recurses */
5131 /* No command-specific prep needed */
5133 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
5134 case AT_EnableAlwaysTrig
:
5135 case AT_EnableReplicaTrig
:
5136 case AT_EnableTrigAll
:
5137 case AT_EnableTrigUser
:
5138 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
5139 case AT_DisableTrigAll
:
5140 case AT_DisableTrigUser
:
5141 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5142 /* Set up recursion for phase 2; no other prep needed */
5144 cmd
->recurse
= true;
5145 pass
= AT_PASS_MISC
;
5147 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
5148 case AT_EnableAlwaysRule
:
5149 case AT_EnableReplicaRule
:
5150 case AT_DisableRule
:
5151 case AT_AddOf
: /* OF */
5152 case AT_DropOf
: /* NOT OF */
5153 case AT_EnableRowSecurity
:
5154 case AT_DisableRowSecurity
:
5155 case AT_ForceRowSecurity
:
5156 case AT_NoForceRowSecurity
:
5157 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5158 /* These commands never recurse */
5159 /* No command-specific prep needed */
5160 pass
= AT_PASS_MISC
;
5162 case AT_GenericOptions
:
5163 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5164 /* No command-specific prep needed */
5165 pass
= AT_PASS_MISC
;
5167 case AT_AttachPartition
:
5168 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_INDEX
);
5169 /* No command-specific prep needed */
5170 pass
= AT_PASS_MISC
;
5172 case AT_DetachPartition
:
5173 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5174 /* No command-specific prep needed */
5175 pass
= AT_PASS_MISC
;
5177 case AT_DetachPartitionFinalize
:
5178 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5179 /* No command-specific prep needed */
5180 pass
= AT_PASS_MISC
;
5182 case AT_SplitPartition
:
5183 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5184 /* No command-specific prep needed */
5185 pass
= AT_PASS_MISC
;
5187 case AT_MergePartitions
:
5188 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5189 /* No command-specific prep needed */
5190 pass
= AT_PASS_MISC
;
5193 elog(ERROR
, "unrecognized alter table type: %d",
5194 (int) cmd
->subtype
);
5195 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
5198 Assert(pass
> AT_PASS_UNSET
);
5200 /* Add the subcommand to the appropriate list for phase 2 */
5201 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
5207 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5208 * dispatched in a "safe" execution order (designed to avoid unnecessary
5212 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
5213 AlterTableUtilityContext
*context
)
5218 * We process all the tables "in parallel", one pass at a time. This is
5219 * needed because we may have to propagate work from one table to another
5220 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5221 * re-adding of the foreign key constraint to the other table). Work can
5222 * only be propagated into later passes, however.
5224 for (AlterTablePass pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
5226 /* Go through each table that needs to be processed */
5227 foreach(ltab
, *wqueue
)
5229 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5230 List
*subcmds
= tab
->subcmds
[pass
];
5237 * Open the relation and store it in tab. This allows subroutines
5238 * close and reopen, if necessary. Appropriate lock was obtained
5239 * by phase 1, needn't get it again.
5241 tab
->rel
= relation_open(tab
->relid
, NoLock
);
5243 foreach(lcmd
, subcmds
)
5244 ATExecCmd(wqueue
, tab
,
5245 lfirst_node(AlterTableCmd
, lcmd
),
5246 lockmode
, pass
, context
);
5249 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5250 * (this is not done in ATExecAlterColumnType since it should be
5251 * done only once if multiple columns of a table are altered).
5253 if (pass
== AT_PASS_ALTER_TYPE
|| pass
== AT_PASS_SET_EXPRESSION
)
5254 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
5258 relation_close(tab
->rel
, NoLock
);
5264 /* Check to see if a toast table must be added. */
5265 foreach(ltab
, *wqueue
)
5267 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5270 * If the table is source table of ATTACH PARTITION command, we did
5271 * not modify anything about it that will change its toasting
5272 * requirement, so no need to check.
5274 if (((tab
->relkind
== RELKIND_RELATION
||
5275 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5276 tab
->partition_constraint
== NULL
) ||
5277 tab
->relkind
== RELKIND_MATVIEW
)
5278 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5283 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5286 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5287 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
5288 AlterTableUtilityContext
*context
)
5290 ObjectAddress address
= InvalidObjectAddress
;
5291 Relation rel
= tab
->rel
;
5293 switch (cmd
->subtype
)
5295 case AT_AddColumn
: /* ADD COLUMN */
5296 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5297 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5298 cmd
->recurse
, false,
5299 lockmode
, cur_pass
, context
);
5301 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5302 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5304 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5305 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5307 case AT_AddIdentity
:
5308 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5310 Assert(cmd
!= NULL
);
5311 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5313 case AT_SetIdentity
:
5314 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5316 Assert(cmd
!= NULL
);
5317 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5319 case AT_DropIdentity
:
5320 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
, cmd
->recurse
, false);
5322 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5323 address
= ATExecDropNotNull(rel
, cmd
->name
, cmd
->recurse
, lockmode
);
5325 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5326 address
= ATExecSetNotNull(wqueue
, rel
, NULL
, cmd
->name
,
5327 cmd
->recurse
, false, NULL
, lockmode
);
5329 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull */
5330 address
= ATExecSetAttNotNull(wqueue
, rel
, cmd
->name
, lockmode
);
5332 case AT_SetExpression
:
5333 address
= ATExecSetExpression(tab
, rel
, cmd
->name
, cmd
->def
, lockmode
);
5335 case AT_DropExpression
:
5336 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5338 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5339 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5341 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5342 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5344 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5345 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5347 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5348 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5350 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5351 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5354 case AT_DropColumn
: /* DROP COLUMN */
5355 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5356 cmd
->behavior
, cmd
->recurse
, false,
5357 cmd
->missing_ok
, lockmode
,
5360 case AT_AddIndex
: /* ADD INDEX */
5361 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5364 case AT_ReAddIndex
: /* ADD INDEX */
5365 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5368 case AT_ReAddStatistics
: /* ADD STATISTICS */
5369 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5372 case AT_AddConstraint
: /* ADD CONSTRAINT */
5373 /* Transform the command only during initial examination */
5374 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5375 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5376 cmd
->recurse
, lockmode
,
5378 /* Depending on constraint type, might be no more work to do now */
5381 ATExecAddConstraint(wqueue
, tab
, rel
,
5382 (Constraint
*) cmd
->def
,
5383 cmd
->recurse
, false, lockmode
);
5385 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5387 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5388 true, true, lockmode
);
5390 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5393 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5394 ((AlterDomainStmt
*) cmd
->def
)->def
,
5397 case AT_ReAddComment
: /* Re-add existing comment */
5398 address
= CommentObject((CommentStmt
*) cmd
->def
);
5400 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5401 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5404 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5405 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5407 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5408 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5411 case AT_DropConstraint
: /* DROP CONSTRAINT */
5412 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5414 cmd
->missing_ok
, lockmode
);
5416 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5417 /* parse transformation was done earlier */
5418 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5420 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5422 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5423 (List
*) cmd
->def
, lockmode
);
5425 case AT_ChangeOwner
: /* ALTER OWNER */
5426 ATExecChangeOwner(RelationGetRelid(rel
),
5427 get_rolespec_oid(cmd
->newowner
, false),
5430 case AT_ClusterOn
: /* CLUSTER ON */
5431 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5433 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5434 ATExecDropCluster(rel
, lockmode
);
5436 case AT_SetLogged
: /* SET LOGGED */
5437 case AT_SetUnLogged
: /* SET UNLOGGED */
5439 case AT_DropOids
: /* SET WITHOUT OIDS */
5440 /* nothing to do here, oid columns don't exist anymore */
5442 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5445 * Only do this for partitioned tables, for which this is just a
5446 * catalog change. Tables with storage are handled by Phase 3.
5448 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
5449 tab
->chgAccessMethod
)
5450 ATExecSetAccessMethodNoStorage(rel
, tab
->newAccessMethod
);
5452 case AT_SetTableSpace
: /* SET TABLESPACE */
5455 * Only do this for partitioned tables and indexes, for which this
5456 * is just a catalog change. Other relation types which have
5457 * storage are handled by Phase 3.
5459 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5460 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5461 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5464 case AT_SetRelOptions
: /* SET (...) */
5465 case AT_ResetRelOptions
: /* RESET (...) */
5466 case AT_ReplaceRelOptions
: /* replace entire option list */
5467 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5469 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5470 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5471 TRIGGER_FIRES_ON_ORIGIN
, false,
5475 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5476 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5477 TRIGGER_FIRES_ALWAYS
, false,
5481 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5482 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5483 TRIGGER_FIRES_ON_REPLICA
, false,
5487 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5488 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5489 TRIGGER_DISABLED
, false,
5493 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5494 ATExecEnableDisableTrigger(rel
, NULL
,
5495 TRIGGER_FIRES_ON_ORIGIN
, false,
5499 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5500 ATExecEnableDisableTrigger(rel
, NULL
,
5501 TRIGGER_DISABLED
, false,
5505 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5506 ATExecEnableDisableTrigger(rel
, NULL
,
5507 TRIGGER_FIRES_ON_ORIGIN
, true,
5511 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5512 ATExecEnableDisableTrigger(rel
, NULL
,
5513 TRIGGER_DISABLED
, true,
5518 case AT_EnableRule
: /* ENABLE RULE name */
5519 ATExecEnableDisableRule(rel
, cmd
->name
,
5520 RULE_FIRES_ON_ORIGIN
, lockmode
);
5522 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5523 ATExecEnableDisableRule(rel
, cmd
->name
,
5524 RULE_FIRES_ALWAYS
, lockmode
);
5526 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5527 ATExecEnableDisableRule(rel
, cmd
->name
,
5528 RULE_FIRES_ON_REPLICA
, lockmode
);
5530 case AT_DisableRule
: /* DISABLE RULE name */
5531 ATExecEnableDisableRule(rel
, cmd
->name
,
5532 RULE_DISABLED
, lockmode
);
5536 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5538 case AT_DropInherit
:
5539 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5542 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5545 ATExecDropOf(rel
, lockmode
);
5547 case AT_ReplicaIdentity
:
5548 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5550 case AT_EnableRowSecurity
:
5551 ATExecSetRowSecurity(rel
, true);
5553 case AT_DisableRowSecurity
:
5554 ATExecSetRowSecurity(rel
, false);
5556 case AT_ForceRowSecurity
:
5557 ATExecForceNoForceRowSecurity(rel
, true);
5559 case AT_NoForceRowSecurity
:
5560 ATExecForceNoForceRowSecurity(rel
, false);
5562 case AT_GenericOptions
:
5563 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5565 case AT_AttachPartition
:
5566 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5568 Assert(cmd
!= NULL
);
5569 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5570 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5573 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5574 ((PartitionCmd
*) cmd
->def
)->name
);
5576 case AT_DetachPartition
:
5577 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5579 Assert(cmd
!= NULL
);
5580 /* ATPrepCmd ensures it must be a table */
5581 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5582 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5583 ((PartitionCmd
*) cmd
->def
)->name
,
5584 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5586 case AT_DetachPartitionFinalize
:
5587 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5589 case AT_SplitPartition
:
5590 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5592 Assert(cmd
!= NULL
);
5593 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5594 ATExecSplitPartition(wqueue
, tab
, rel
, (PartitionCmd
*) cmd
->def
,
5597 case AT_MergePartitions
:
5598 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5600 Assert(cmd
!= NULL
);
5601 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5602 ATExecMergePartitions(wqueue
, tab
, rel
, (PartitionCmd
*) cmd
->def
,
5606 elog(ERROR
, "unrecognized alter table type: %d",
5607 (int) cmd
->subtype
);
5612 * Report the subcommand to interested event triggers.
5615 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5618 * Bump the command counter to ensure the next subcommand in the sequence
5619 * can see the changes so far
5621 CommandCounterIncrement();
5625 * ATParseTransformCmd: perform parse transformation for one subcommand
5627 * Returns the transformed subcommand tree, if there is one, else NULL.
5629 * The parser may hand back additional AlterTableCmd(s) and/or other
5630 * utility statements, either before or after the original subcommand.
5631 * Other AlterTableCmds are scheduled into the appropriate slot of the
5632 * AlteredTableInfo (they had better be for later passes than the current one).
5633 * Utility statements that are supposed to happen before the AlterTableCmd
5634 * are executed immediately. Those that are supposed to happen afterwards
5635 * are added to the tab->afterStmts list to be done at the very end.
5637 static AlterTableCmd
*
5638 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5639 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5640 AlterTablePass cur_pass
, AlterTableUtilityContext
*context
)
5642 AlterTableCmd
*newcmd
= NULL
;
5643 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5648 /* Gin up an AlterTableStmt with just this subcommand and this table */
5650 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5651 pstrdup(RelationGetRelationName(rel
)),
5653 atstmt
->relation
->inh
= recurse
;
5654 atstmt
->cmds
= list_make1(cmd
);
5655 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5656 atstmt
->missing_ok
= false;
5658 /* Transform the AlterTableStmt */
5659 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5661 context
->queryString
,
5665 /* Execute any statements that should happen before these subcommand(s) */
5666 foreach(lc
, beforeStmts
)
5668 Node
*stmt
= (Node
*) lfirst(lc
);
5670 ProcessUtilityForAlterTable(stmt
, context
);
5671 CommandCounterIncrement();
5674 /* Examine the transformed subcommands and schedule them appropriately */
5675 foreach(lc
, atstmt
->cmds
)
5677 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5678 AlterTablePass pass
;
5681 * This switch need only cover the subcommand types that can be added
5682 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5683 * executing the subcommand immediately, as a substitute for the
5684 * original subcommand. (Note, however, that this does cause
5685 * AT_AddConstraint subcommands to be rescheduled into later passes,
5686 * which is important for index and foreign key constraints.)
5688 * We assume we needn't do any phase-1 checks for added subcommands.
5690 switch (cmd2
->subtype
)
5692 case AT_SetAttNotNull
:
5693 ATSimpleRecursion(wqueue
, rel
, cmd2
, recurse
, lockmode
, context
);
5694 pass
= AT_PASS_COL_ATTRS
;
5699 * A primary key on an inheritance parent needs supporting NOT
5700 * NULL constraint on its children; enqueue commands to create
5701 * those or mark them inherited if they already exist.
5703 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5704 pass
= AT_PASS_ADD_INDEX
;
5706 case AT_AddIndexConstraint
:
5708 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5709 pass
= AT_PASS_ADD_INDEXCONSTR
;
5711 case AT_AddConstraint
:
5712 /* Recursion occurs during execution phase */
5714 cmd2
->recurse
= true;
5715 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5717 case CONSTR_PRIMARY
:
5719 case CONSTR_EXCLUSION
:
5720 pass
= AT_PASS_ADD_INDEXCONSTR
;
5723 pass
= AT_PASS_ADD_OTHERCONSTR
;
5727 case AT_AlterColumnGenericOptions
:
5728 /* This command never recurses */
5729 /* No command-specific prep needed */
5730 pass
= AT_PASS_MISC
;
5737 if (pass
< cur_pass
)
5739 /* Cannot schedule into a pass we already finished */
5740 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5743 else if (pass
> cur_pass
)
5745 /* OK, queue it up for later */
5746 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5751 * We should see at most one subcommand for the current pass,
5752 * which is the transformed version of the original subcommand.
5754 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5756 /* Found the transformed version of our subcommand */
5760 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5765 /* Queue up any after-statements to happen at the end */
5766 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5772 * ATRewriteTables: ALTER TABLE phase 3
5775 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5776 AlterTableUtilityContext
*context
)
5780 /* Go through each table that needs to be checked or rewritten */
5781 foreach(ltab
, *wqueue
)
5783 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5785 /* Relations without storage may be ignored here */
5786 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5790 * If we change column data types, the operation has to be propagated
5791 * to tables that use this table's rowtype as a column type.
5792 * tab->newvals will also be non-NULL in the case where we're adding a
5793 * column with a default. We choose to forbid that case as well,
5794 * since composite types might eventually support defaults.
5796 * (Eventually we'll probably need to check for composite type
5797 * dependencies even when we're just scanning the table without a
5798 * rewrite, but at the moment a composite type does not enforce any
5799 * constraints, so it's not necessary/appropriate to enforce them just
5802 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5806 rel
= table_open(tab
->relid
, NoLock
);
5807 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5808 table_close(rel
, NoLock
);
5812 * We only need to rewrite the table if at least one column needs to
5813 * be recomputed, or we are changing its persistence or access method.
5815 * There are two reasons for requiring a rewrite when changing
5816 * persistence: on one hand, we need to ensure that the buffers
5817 * belonging to each of the two relations are marked with or without
5818 * BM_PERMANENT properly. On the other hand, since rewriting creates
5819 * and assigns a new relfilenumber, we automatically create or drop an
5820 * init fork for the relation as appropriate.
5822 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5824 /* Build a temporary relation and copy data */
5827 Oid NewAccessMethod
;
5831 OldHeap
= table_open(tab
->relid
, NoLock
);
5834 * We don't support rewriting of system catalogs; there are too
5835 * many corner cases and too little benefit. In particular this
5836 * is certainly not going to work for mapped catalogs.
5838 if (IsSystemRelation(OldHeap
))
5840 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5841 errmsg("cannot rewrite system relation \"%s\"",
5842 RelationGetRelationName(OldHeap
))));
5844 if (RelationIsUsedAsCatalogTable(OldHeap
))
5846 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5847 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5848 RelationGetRelationName(OldHeap
))));
5851 * Don't allow rewrite on temp tables of other backends ... their
5852 * local buffer manager is not going to cope.
5854 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5856 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5857 errmsg("cannot rewrite temporary tables of other sessions")));
5860 * Select destination tablespace (same as original unless user
5861 * requested a change)
5863 if (tab
->newTableSpace
)
5864 NewTableSpace
= tab
->newTableSpace
;
5866 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5869 * Select destination access method (same as original unless user
5870 * requested a change)
5872 if (tab
->chgAccessMethod
)
5873 NewAccessMethod
= tab
->newAccessMethod
;
5875 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5878 * Select persistence of transient table (same as original unless
5879 * user requested a change)
5881 persistence
= tab
->chgPersistence
?
5882 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5884 table_close(OldHeap
, NoLock
);
5887 * Fire off an Event Trigger now, before actually rewriting the
5890 * We don't support Event Trigger for nested commands anywhere,
5891 * here included, and parsetree is given NULL when coming from
5892 * AlterTableInternal.
5894 * And fire it only once.
5897 EventTriggerTableRewrite((Node
*) parsetree
,
5902 * Create transient table that will receive the modified data.
5904 * Ensure it is marked correctly as logged or unlogged. We have
5905 * to do this here so that buffers for the new relfilenumber will
5906 * have the right persistence set, and at the same time ensure
5907 * that the original filenumbers's buffers will get read in with
5908 * the correct setting (i.e. the original one). Otherwise a
5909 * rollback after the rewrite would possibly result with buffers
5910 * for the original filenumbers having the wrong persistence
5913 * NB: This relies on swap_relation_files() also swapping the
5914 * persistence. That wouldn't work for pg_class, but that can't be
5917 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5918 persistence
, lockmode
);
5921 * Copy the heap data into the new table with the desired
5922 * modifications, and test the current data within the table
5923 * against new constraints generated by ALTER TABLE commands.
5925 ATRewriteTable(tab
, OIDNewHeap
, lockmode
);
5928 * Swap the physical files of the old and new heaps, then rebuild
5929 * indexes and discard the old heap. We can use RecentXmin for
5930 * the table's new relfrozenxid because we rewrote all the tuples
5931 * in ATRewriteTable, so no older Xid remains in the table. Also,
5932 * we never try to swap toast tables by content, since we have no
5933 * interest in letting this code work on system catalogs.
5935 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5937 !OidIsValid(tab
->newTableSpace
),
5939 ReadNextMultiXactId(),
5942 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5944 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5946 if (tab
->chgPersistence
)
5947 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5952 * If required, test the current data within the table against new
5953 * constraints generated by ALTER TABLE commands, but don't
5956 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5957 tab
->partition_constraint
!= NULL
)
5958 ATRewriteTable(tab
, InvalidOid
, lockmode
);
5961 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5962 * just do a block-by-block copy.
5964 if (tab
->newTableSpace
)
5965 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5969 * Also change persistence of owned sequences, so that it matches the
5970 * table persistence.
5972 if (tab
->chgPersistence
)
5974 List
*seqlist
= getOwnedSequences(tab
->relid
);
5977 foreach(lc
, seqlist
)
5979 Oid seq_relid
= lfirst_oid(lc
);
5981 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5987 * Foreign key constraints are checked in a final pass, since (a) it's
5988 * generally best to examine each one separately, and (b) it's at least
5989 * theoretically possible that we have changed both relations of the
5990 * foreign key, and we'd better have finished both rewrites before we try
5991 * to read the tables.
5993 foreach(ltab
, *wqueue
)
5995 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5996 Relation rel
= NULL
;
5999 /* Relations without storage may be ignored here too */
6000 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
6003 foreach(lcon
, tab
->constraints
)
6005 NewConstraint
*con
= lfirst(lcon
);
6007 if (con
->contype
== CONSTR_FOREIGN
)
6009 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
6014 /* Long since locked, no need for another */
6015 rel
= table_open(tab
->relid
, NoLock
);
6018 refrel
= table_open(con
->refrelid
, RowShareLock
);
6020 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
6023 con
->conwithperiod
);
6026 * No need to mark the constraint row as validated, we did
6027 * that when we inserted the row earlier.
6030 table_close(refrel
, NoLock
);
6035 table_close(rel
, NoLock
);
6038 /* Finally, run any afterStmts that were queued up */
6039 foreach(ltab
, *wqueue
)
6041 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
6044 foreach(lc
, tab
->afterStmts
)
6046 Node
*stmt
= (Node
*) lfirst(lc
);
6048 ProcessUtilityForAlterTable(stmt
, context
);
6049 CommandCounterIncrement();
6055 * ATRewriteTable: scan or rewrite one table
6057 * OIDNewHeap is InvalidOid if we don't need to rewrite
6060 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
)
6064 TupleDesc oldTupDesc
;
6065 TupleDesc newTupDesc
;
6066 bool needscan
= false;
6067 List
*notnull_attrs
;
6072 BulkInsertState bistate
;
6074 ExprState
*partqualstate
= NULL
;
6077 * Open the relation(s). We have surely already locked the existing
6080 oldrel
= table_open(tab
->relid
, NoLock
);
6081 oldTupDesc
= tab
->oldDesc
;
6082 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
6084 if (OidIsValid(OIDNewHeap
))
6085 newrel
= table_open(OIDNewHeap
, lockmode
);
6090 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6091 * is empty, so don't bother using it.
6095 mycid
= GetCurrentCommandId(true);
6096 bistate
= GetBulkInsertState();
6097 ti_options
= TABLE_INSERT_SKIP_FSM
;
6101 /* keep compiler quiet about using these uninitialized */
6108 * Generate the constraint and default execution states
6111 estate
= CreateExecutorState();
6113 /* Build the needed expression execution states */
6114 foreach(l
, tab
->constraints
)
6116 NewConstraint
*con
= lfirst(l
);
6118 switch (con
->contype
)
6122 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
6124 case CONSTR_FOREIGN
:
6125 /* Nothing to do here */
6128 elog(ERROR
, "unrecognized constraint type: %d",
6129 (int) con
->contype
);
6133 /* Build expression execution states for partition check quals */
6134 if (tab
->partition_constraint
)
6137 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
6140 foreach(l
, tab
->newvals
)
6142 NewColumnValue
*ex
= lfirst(l
);
6144 /* expr already planned */
6145 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
6148 notnull_attrs
= NIL
;
6149 if (newrel
|| tab
->verify_new_notnull
)
6152 * If we are rebuilding the tuples OR if we added any new but not
6153 * verified not-null constraints, check all not-null constraints. This
6154 * is a bit of overkill but it minimizes risk of bugs, and
6155 * heap_attisnull is a pretty cheap test anyway.
6157 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6159 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
6161 if (attr
->attnotnull
&& !attr
->attisdropped
)
6162 notnull_attrs
= lappend_int(notnull_attrs
, i
);
6168 if (newrel
|| needscan
)
6170 ExprContext
*econtext
;
6171 TupleTableSlot
*oldslot
;
6172 TupleTableSlot
*newslot
;
6174 MemoryContext oldCxt
;
6175 List
*dropped_attrs
= NIL
;
6181 (errmsg_internal("rewriting table \"%s\"",
6182 RelationGetRelationName(oldrel
))));
6185 (errmsg_internal("verifying table \"%s\"",
6186 RelationGetRelationName(oldrel
))));
6191 * All predicate locks on the tuples or pages are about to be made
6192 * invalid, because we move tuples around. Promote them to
6195 TransferPredicateLocksToHeapRelation(oldrel
);
6198 econtext
= GetPerTupleExprContext(estate
);
6201 * Create necessary tuple slots. When rewriting, two slots are needed,
6202 * otherwise one suffices. In the case where one slot suffices, we
6203 * need to use the new tuple descriptor, otherwise some constraints
6204 * can't be evaluated. Note that even when the tuple layout is the
6205 * same and no rewrite is required, the tupDescs might not be
6206 * (consider ADD COLUMN without a default).
6210 Assert(newrel
!= NULL
);
6211 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
6212 table_slot_callbacks(oldrel
));
6213 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
6214 table_slot_callbacks(newrel
));
6217 * Set all columns in the new slot to NULL initially, to ensure
6218 * columns added as part of the rewrite are initialized to NULL.
6219 * That is necessary as tab->newvals will not contain an
6220 * expression for columns with a NULL default, e.g. when adding a
6221 * column without a default together with a column with a default
6222 * requiring an actual rewrite.
6224 ExecStoreAllNullTuple(newslot
);
6228 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
6229 table_slot_callbacks(oldrel
));
6234 * Any attributes that are dropped according to the new tuple
6235 * descriptor can be set to NULL. We precompute the list of dropped
6236 * attributes to avoid needing to do so in the per-tuple loop.
6238 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6240 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
6241 dropped_attrs
= lappend_int(dropped_attrs
, i
);
6245 * Scan through the rows, generating a new row if needed and then
6246 * checking all the constraints.
6248 snapshot
= RegisterSnapshot(GetLatestSnapshot());
6249 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
6252 * Switch to per-tuple memory context and reset it for each tuple
6253 * produced, so we don't leak memory.
6255 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
6257 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
6259 TupleTableSlot
*insertslot
;
6261 if (tab
->rewrite
> 0)
6263 /* Extract data from old tuple */
6264 slot_getallattrs(oldslot
);
6265 ExecClearTuple(newslot
);
6267 /* copy attributes */
6268 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
6269 sizeof(Datum
) * oldslot
->tts_nvalid
);
6270 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
6271 sizeof(bool) * oldslot
->tts_nvalid
);
6273 /* Set dropped attributes to null in new tuple */
6274 foreach(lc
, dropped_attrs
)
6275 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
6278 * Constraints and GENERATED expressions might reference the
6279 * tableoid column, so fill tts_tableOid with the desired
6280 * value. (We must do this each time, because it gets
6281 * overwritten with newrel's OID during storing.)
6283 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
6286 * Process supplied expressions to replace selected columns.
6288 * First, evaluate expressions whose inputs come from the old
6291 econtext
->ecxt_scantuple
= oldslot
;
6293 foreach(l
, tab
->newvals
)
6295 NewColumnValue
*ex
= lfirst(l
);
6297 if (ex
->is_generated
)
6300 newslot
->tts_values
[ex
->attnum
- 1]
6301 = ExecEvalExpr(ex
->exprstate
,
6303 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6306 ExecStoreVirtualTuple(newslot
);
6309 * Now, evaluate any expressions whose inputs come from the
6310 * new tuple. We assume these columns won't reference each
6311 * other, so that there's no ordering dependency.
6313 econtext
->ecxt_scantuple
= newslot
;
6315 foreach(l
, tab
->newvals
)
6317 NewColumnValue
*ex
= lfirst(l
);
6319 if (!ex
->is_generated
)
6322 newslot
->tts_values
[ex
->attnum
- 1]
6323 = ExecEvalExpr(ex
->exprstate
,
6325 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6328 insertslot
= newslot
;
6333 * If there's no rewrite, old and new table are guaranteed to
6334 * have the same AM, so we can just use the old slot to verify
6335 * new constraints etc.
6337 insertslot
= oldslot
;
6340 /* Now check any constraints on the possibly-changed tuple */
6341 econtext
->ecxt_scantuple
= insertslot
;
6343 foreach(l
, notnull_attrs
)
6345 int attn
= lfirst_int(l
);
6347 if (slot_attisnull(insertslot
, attn
+ 1))
6349 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6352 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6353 errmsg("column \"%s\" of relation \"%s\" contains null values",
6354 NameStr(attr
->attname
),
6355 RelationGetRelationName(oldrel
)),
6356 errtablecol(oldrel
, attn
+ 1)));
6360 foreach(l
, tab
->constraints
)
6362 NewConstraint
*con
= lfirst(l
);
6364 switch (con
->contype
)
6367 if (!ExecCheck(con
->qualstate
, econtext
))
6369 (errcode(ERRCODE_CHECK_VIOLATION
),
6370 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6372 RelationGetRelationName(oldrel
)),
6373 errtableconstraint(oldrel
, con
->name
)));
6375 case CONSTR_NOTNULL
:
6376 case CONSTR_FOREIGN
:
6377 /* Nothing to do here */
6380 elog(ERROR
, "unrecognized constraint type: %d",
6381 (int) con
->contype
);
6385 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6387 if (tab
->validate_default
)
6389 (errcode(ERRCODE_CHECK_VIOLATION
),
6390 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6391 RelationGetRelationName(oldrel
)),
6395 (errcode(ERRCODE_CHECK_VIOLATION
),
6396 errmsg("partition constraint of relation \"%s\" is violated by some row",
6397 RelationGetRelationName(oldrel
)),
6401 /* Write the tuple out to the new relation */
6403 table_tuple_insert(newrel
, insertslot
, mycid
,
6404 ti_options
, bistate
);
6406 ResetExprContext(econtext
);
6408 CHECK_FOR_INTERRUPTS();
6411 MemoryContextSwitchTo(oldCxt
);
6412 table_endscan(scan
);
6413 UnregisterSnapshot(snapshot
);
6415 ExecDropSingleTupleTableSlot(oldslot
);
6417 ExecDropSingleTupleTableSlot(newslot
);
6420 FreeExecutorState(estate
);
6422 table_close(oldrel
, NoLock
);
6425 FreeBulkInsertState(bistate
);
6427 table_finish_bulk_insert(newrel
, ti_options
);
6429 table_close(newrel
, NoLock
);
6434 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6436 static AlteredTableInfo
*
6437 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6439 Oid relid
= RelationGetRelid(rel
);
6440 AlteredTableInfo
*tab
;
6443 foreach(ltab
, *wqueue
)
6445 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6446 if (tab
->relid
== relid
)
6451 * Not there, so add it. Note that we make a copy of the relation's
6452 * existing descriptor before anything interesting can happen to it.
6454 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6456 tab
->rel
= NULL
; /* set later */
6457 tab
->relkind
= rel
->rd_rel
->relkind
;
6458 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6459 tab
->newAccessMethod
= InvalidOid
;
6460 tab
->chgAccessMethod
= false;
6461 tab
->newTableSpace
= InvalidOid
;
6462 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6463 tab
->chgPersistence
= false;
6465 *wqueue
= lappend(*wqueue
, tab
);
6471 alter_table_type_to_string(AlterTableType cmdtype
)
6476 case AT_AddColumnToView
:
6477 return "ADD COLUMN";
6478 case AT_ColumnDefault
:
6479 case AT_CookedColumnDefault
:
6480 return "ALTER COLUMN ... SET DEFAULT";
6481 case AT_DropNotNull
:
6482 return "ALTER COLUMN ... DROP NOT NULL";
6484 return "ALTER COLUMN ... SET NOT NULL";
6485 case AT_SetAttNotNull
:
6486 return NULL
; /* not real grammar */
6487 case AT_SetExpression
:
6488 return "ALTER COLUMN ... SET EXPRESSION";
6489 case AT_DropExpression
:
6490 return "ALTER COLUMN ... DROP EXPRESSION";
6491 case AT_SetStatistics
:
6492 return "ALTER COLUMN ... SET STATISTICS";
6494 return "ALTER COLUMN ... SET";
6495 case AT_ResetOptions
:
6496 return "ALTER COLUMN ... RESET";
6498 return "ALTER COLUMN ... SET STORAGE";
6499 case AT_SetCompression
:
6500 return "ALTER COLUMN ... SET COMPRESSION";
6502 return "DROP COLUMN";
6505 return NULL
; /* not real grammar */
6506 case AT_AddConstraint
:
6507 case AT_ReAddConstraint
:
6508 case AT_ReAddDomainConstraint
:
6509 case AT_AddIndexConstraint
:
6510 return "ADD CONSTRAINT";
6511 case AT_AlterConstraint
:
6512 return "ALTER CONSTRAINT";
6513 case AT_ValidateConstraint
:
6514 return "VALIDATE CONSTRAINT";
6515 case AT_DropConstraint
:
6516 return "DROP CONSTRAINT";
6517 case AT_ReAddComment
:
6518 return NULL
; /* not real grammar */
6519 case AT_AlterColumnType
:
6520 return "ALTER COLUMN ... SET DATA TYPE";
6521 case AT_AlterColumnGenericOptions
:
6522 return "ALTER COLUMN ... OPTIONS";
6523 case AT_ChangeOwner
:
6526 return "CLUSTER ON";
6527 case AT_DropCluster
:
6528 return "SET WITHOUT CLUSTER";
6529 case AT_SetAccessMethod
:
6530 return "SET ACCESS METHOD";
6532 return "SET LOGGED";
6533 case AT_SetUnLogged
:
6534 return "SET UNLOGGED";
6536 return "SET WITHOUT OIDS";
6537 case AT_SetTableSpace
:
6538 return "SET TABLESPACE";
6539 case AT_SetRelOptions
:
6541 case AT_ResetRelOptions
:
6543 case AT_ReplaceRelOptions
:
6544 return NULL
; /* not real grammar */
6546 return "ENABLE TRIGGER";
6547 case AT_EnableAlwaysTrig
:
6548 return "ENABLE ALWAYS TRIGGER";
6549 case AT_EnableReplicaTrig
:
6550 return "ENABLE REPLICA TRIGGER";
6551 case AT_DisableTrig
:
6552 return "DISABLE TRIGGER";
6553 case AT_EnableTrigAll
:
6554 return "ENABLE TRIGGER ALL";
6555 case AT_DisableTrigAll
:
6556 return "DISABLE TRIGGER ALL";
6557 case AT_EnableTrigUser
:
6558 return "ENABLE TRIGGER USER";
6559 case AT_DisableTrigUser
:
6560 return "DISABLE TRIGGER USER";
6562 return "ENABLE RULE";
6563 case AT_EnableAlwaysRule
:
6564 return "ENABLE ALWAYS RULE";
6565 case AT_EnableReplicaRule
:
6566 return "ENABLE REPLICA RULE";
6567 case AT_DisableRule
:
6568 return "DISABLE RULE";
6571 case AT_DropInherit
:
6572 return "NO INHERIT";
6577 case AT_ReplicaIdentity
:
6578 return "REPLICA IDENTITY";
6579 case AT_EnableRowSecurity
:
6580 return "ENABLE ROW SECURITY";
6581 case AT_DisableRowSecurity
:
6582 return "DISABLE ROW SECURITY";
6583 case AT_ForceRowSecurity
:
6584 return "FORCE ROW SECURITY";
6585 case AT_NoForceRowSecurity
:
6586 return "NO FORCE ROW SECURITY";
6587 case AT_GenericOptions
:
6589 case AT_AttachPartition
:
6590 return "ATTACH PARTITION";
6591 case AT_DetachPartition
:
6592 return "DETACH PARTITION";
6593 case AT_DetachPartitionFinalize
:
6594 return "DETACH PARTITION ... FINALIZE";
6595 case AT_SplitPartition
:
6596 return "SPLIT PARTITION";
6597 case AT_MergePartitions
:
6598 return "MERGE PARTITIONS";
6599 case AT_AddIdentity
:
6600 return "ALTER COLUMN ... ADD IDENTITY";
6601 case AT_SetIdentity
:
6602 return "ALTER COLUMN ... SET";
6603 case AT_DropIdentity
:
6604 return "ALTER COLUMN ... DROP IDENTITY";
6605 case AT_ReAddStatistics
:
6606 return NULL
; /* not real grammar */
6613 * ATSimplePermissions
6615 * - Ensure that it is a relation (or possibly a view)
6616 * - Ensure this user is the owner
6617 * - Ensure that it is not a system table
6620 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6624 switch (rel
->rd_rel
->relkind
)
6626 case RELKIND_RELATION
:
6627 case RELKIND_PARTITIONED_TABLE
:
6628 actual_target
= ATT_TABLE
;
6631 actual_target
= ATT_VIEW
;
6633 case RELKIND_MATVIEW
:
6634 actual_target
= ATT_MATVIEW
;
6637 actual_target
= ATT_INDEX
;
6639 case RELKIND_PARTITIONED_INDEX
:
6640 actual_target
= ATT_PARTITIONED_INDEX
;
6642 case RELKIND_COMPOSITE_TYPE
:
6643 actual_target
= ATT_COMPOSITE_TYPE
;
6645 case RELKIND_FOREIGN_TABLE
:
6646 actual_target
= ATT_FOREIGN_TABLE
;
6648 case RELKIND_SEQUENCE
:
6649 actual_target
= ATT_SEQUENCE
;
6656 /* Wrong target type? */
6657 if ((actual_target
& allowed_targets
) == 0)
6659 const char *action_str
= alter_table_type_to_string(cmdtype
);
6663 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6664 /* translator: %s is a group of some SQL keywords */
6665 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6666 action_str
, RelationGetRelationName(rel
)),
6667 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6669 /* internal error? */
6670 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6671 RelationGetRelationName(rel
));
6674 /* Permissions checks */
6675 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6676 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6677 RelationGetRelationName(rel
));
6679 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6681 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6682 errmsg("permission denied: \"%s\" is a system catalog",
6683 RelationGetRelationName(rel
))));
6689 * Simple table recursion sufficient for most ALTER TABLE operations.
6690 * All direct and indirect children are processed in an unspecified order.
6691 * Note that if a child inherits from the original table via multiple
6692 * inheritance paths, it will be visited just once.
6695 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6696 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6697 AlterTableUtilityContext
*context
)
6700 * Propagate to children, if desired and if there are (or might be) any
6703 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6705 Oid relid
= RelationGetRelid(rel
);
6709 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6712 * find_all_inheritors does the recursive search of the inheritance
6713 * hierarchy, so all we have to do is process all of the relids in the
6714 * list that it returns.
6716 foreach(child
, children
)
6718 Oid childrelid
= lfirst_oid(child
);
6721 if (childrelid
== relid
)
6723 /* find_all_inheritors already got lock */
6724 childrel
= relation_open(childrelid
, NoLock
);
6725 CheckTableNotInUse(childrel
, "ALTER TABLE");
6726 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6727 relation_close(childrel
, NoLock
);
6733 * Obtain list of partitions of the given table, locking them all at the given
6734 * lockmode and ensuring that they all pass CheckTableNotInUse.
6736 * This function is a no-op if the given relation is not a partitioned table;
6737 * in particular, nothing is done if it's a legacy inheritance parent.
6740 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6742 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6747 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6748 /* first element is the parent rel; must ignore it */
6749 for_each_from(cell
, inh
, 1)
6753 /* find_all_inheritors already got lock */
6754 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6755 CheckTableNotInUse(childrel
, "ALTER TABLE");
6756 table_close(childrel
, NoLock
);
6763 * ATTypedTableRecursion
6765 * Propagate ALTER TYPE operations to the typed tables of that type.
6766 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6767 * recursion to inheritance children of the typed tables.
6770 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6771 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6776 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6778 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6779 RelationGetRelationName(rel
),
6782 foreach(child
, children
)
6784 Oid childrelid
= lfirst_oid(child
);
6787 childrel
= relation_open(childrelid
, lockmode
);
6788 CheckTableNotInUse(childrel
, "ALTER TABLE");
6789 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6790 relation_close(childrel
, NoLock
);
6796 * find_composite_type_dependencies
6798 * Check to see if the type "typeOid" is being used as a column in some table
6799 * (possibly nested several levels deep in composite types, arrays, etc!).
6800 * Eventually, we'd like to propagate the check or rewrite operation
6801 * into such tables, but for now, just error out if we find any.
6803 * Caller should provide either the associated relation of a rowtype,
6804 * or a type name (not both) for use in the error message, if any.
6806 * Note that "typeOid" is not necessarily a composite type; it could also be
6807 * another container type such as an array or range, or a domain over one of
6808 * these things. The name of this function is therefore somewhat historical,
6809 * but it's not worth changing.
6811 * We assume that functions and views depending on the type are not reasons
6812 * to reject the ALTER. (How safe is this really?)
6815 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6816 const char *origTypeName
)
6820 SysScanDesc depScan
;
6823 /* since this function recurses, it could be driven to stack overflow */
6824 check_stack_depth();
6827 * We scan pg_depend to find those things that depend on the given type.
6828 * (We assume we can ignore refobjsubid for a type.)
6830 depRel
= table_open(DependRelationId
, AccessShareLock
);
6832 ScanKeyInit(&key
[0],
6833 Anum_pg_depend_refclassid
,
6834 BTEqualStrategyNumber
, F_OIDEQ
,
6835 ObjectIdGetDatum(TypeRelationId
));
6836 ScanKeyInit(&key
[1],
6837 Anum_pg_depend_refobjid
,
6838 BTEqualStrategyNumber
, F_OIDEQ
,
6839 ObjectIdGetDatum(typeOid
));
6841 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6844 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6846 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6848 TupleDesc tupleDesc
;
6849 Form_pg_attribute att
;
6851 /* Check for directly dependent types */
6852 if (pg_depend
->classid
== TypeRelationId
)
6855 * This must be an array, domain, or range containing the given
6856 * type, so recursively check for uses of this type. Note that
6857 * any error message will mention the original type not the
6858 * container; this is intentional.
6860 find_composite_type_dependencies(pg_depend
->objid
,
6861 origRelation
, origTypeName
);
6865 /* Else, ignore dependees that aren't relations */
6866 if (pg_depend
->classid
!= RelationRelationId
)
6869 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6870 tupleDesc
= RelationGetDescr(rel
);
6873 * If objsubid identifies a specific column, refer to that in error
6874 * messages. Otherwise, search to see if there's a user column of the
6875 * type. (We assume system columns are never of interesting types.)
6876 * The search is needed because an index containing an expression
6877 * column of the target type will just be recorded as a whole-relation
6878 * dependency. If we do not find a column of the type, the dependency
6879 * must indicate that the type is transiently referenced in an index
6880 * expression but not stored on disk, which we assume is OK, just as
6881 * we do for references in views. (It could also be that the target
6882 * type is embedded in some container type that is stored in an index
6883 * column, but the previous recursion should catch such cases.)
6885 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6886 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6890 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6892 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6893 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6899 /* No such column, so assume OK */
6900 relation_close(rel
, AccessShareLock
);
6906 * We definitely should reject if the relation has storage. If it's
6907 * partitioned, then perhaps we don't have to reject: if there are
6908 * partitions then we'll fail when we find one, else there is no
6909 * stored data to worry about. However, it's possible that the type
6910 * change would affect conclusions about whether the type is sortable
6911 * or hashable and thus (if it's a partitioning column) break the
6912 * partitioning rule. For now, reject for partitioned rels too.
6914 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6915 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6920 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6922 RelationGetRelationName(rel
),
6923 NameStr(att
->attname
))));
6924 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6926 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6927 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6928 RelationGetRelationName(origRelation
),
6929 RelationGetRelationName(rel
),
6930 NameStr(att
->attname
))));
6931 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6933 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6934 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6935 RelationGetRelationName(origRelation
),
6936 RelationGetRelationName(rel
),
6937 NameStr(att
->attname
))));
6940 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6941 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6942 RelationGetRelationName(origRelation
),
6943 RelationGetRelationName(rel
),
6944 NameStr(att
->attname
))));
6946 else if (OidIsValid(rel
->rd_rel
->reltype
))
6949 * A view or composite type itself isn't a problem, but we must
6950 * recursively check for indirect dependencies via its rowtype.
6952 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6953 origRelation
, origTypeName
);
6956 relation_close(rel
, AccessShareLock
);
6959 systable_endscan(depScan
);
6961 relation_close(depRel
, AccessShareLock
);
6966 * find_typed_table_dependencies
6968 * Check to see if a composite type is being used as the type of a
6969 * typed table. Abort if any are found and behavior is RESTRICT.
6970 * Else return the list of tables.
6973 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6981 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6983 ScanKeyInit(&key
[0],
6984 Anum_pg_class_reloftype
,
6985 BTEqualStrategyNumber
, F_OIDEQ
,
6986 ObjectIdGetDatum(typeOid
));
6988 scan
= table_beginscan_catalog(classRel
, 1, key
);
6990 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6992 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6994 if (behavior
== DROP_RESTRICT
)
6996 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6997 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6999 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7001 result
= lappend_oid(result
, classform
->oid
);
7004 table_endscan(scan
);
7005 table_close(classRel
, AccessShareLock
);
7014 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7015 * isn't suitable, throw an error. Currently, we require that the type
7016 * originated with CREATE TYPE AS. We could support any row type, but doing so
7017 * would require handling a number of extra corner cases in the DDL commands.
7018 * (Also, allowing domain-over-composite would open up a can of worms about
7019 * whether and how the domain's constraints should apply to derived tables.)
7022 check_of_type(HeapTuple typetuple
)
7024 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
7025 bool typeOk
= false;
7027 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
7029 Relation typeRelation
;
7031 Assert(OidIsValid(typ
->typrelid
));
7032 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
7033 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
7036 * Close the parent rel, but keep our AccessShareLock on it until xact
7037 * commit. That will prevent someone else from deleting or ALTERing
7038 * the type before the typed table creation/conversion commits.
7040 relation_close(typeRelation
, NoLock
);
7044 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7045 errmsg("type %s is not a composite type",
7046 format_type_be(typ
->oid
))));
7051 * ALTER TABLE ADD COLUMN
7053 * Adds an additional attribute to a relation making the assumption that
7054 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7055 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7058 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7059 * have to decide at runtime whether to recurse or not depending on whether we
7060 * actually add a column or merely merge with an existing column. (We can't
7061 * check this in a static pre-pass because it won't handle multiple inheritance
7062 * situations correctly.)
7065 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
7066 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
7067 AlterTableUtilityContext
*context
)
7069 if (rel
->rd_rel
->reloftype
&& !recursing
)
7071 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7072 errmsg("cannot add column to typed table")));
7074 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
7075 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
7077 if (recurse
&& !is_view
)
7078 cmd
->recurse
= true;
7082 * Add a column to a table. The return value is the address of the
7083 * new column in the parent relation.
7085 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7086 * copy (but that happens only after we check for IF NOT EXISTS).
7088 static ObjectAddress
7089 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
7090 AlterTableCmd
**cmd
, bool recurse
, bool recursing
,
7091 LOCKMODE lockmode
, AlterTablePass cur_pass
,
7092 AlterTableUtilityContext
*context
)
7094 Oid myrelid
= RelationGetRelid(rel
);
7095 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7096 bool if_not_exists
= (*cmd
)->missing_ok
;
7100 Form_pg_attribute attribute
;
7106 AlterTableCmd
*childcmd
;
7107 ObjectAddress address
;
7110 /* since this function recurses, it could be driven to stack overflow */
7111 check_stack_depth();
7113 /* At top level, permission check was done in ATPrepCmd, else do it */
7115 ATSimplePermissions((*cmd
)->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7117 if (rel
->rd_rel
->relispartition
&& !recursing
)
7119 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7120 errmsg("cannot add column to a partition")));
7122 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
7125 * Are we adding the column to a recursion child? If so, check whether to
7126 * merge with an existing definition for the column. If we do merge, we
7127 * must not recurse. Children will already have the column, and recursing
7128 * into them would mess up attinhcount.
7130 if (colDef
->inhcount
> 0)
7134 /* Does child already have a column by this name? */
7135 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
7136 if (HeapTupleIsValid(tuple
))
7138 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7143 /* Child column must match on type, typmod, and collation */
7144 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
7145 if (ctypeId
!= childatt
->atttypid
||
7146 ctypmod
!= childatt
->atttypmod
)
7148 (errcode(ERRCODE_DATATYPE_MISMATCH
),
7149 errmsg("child table \"%s\" has different type for column \"%s\"",
7150 RelationGetRelationName(rel
), colDef
->colname
)));
7151 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
7152 if (ccollid
!= childatt
->attcollation
)
7154 (errcode(ERRCODE_COLLATION_MISMATCH
),
7155 errmsg("child table \"%s\" has different collation for column \"%s\"",
7156 RelationGetRelationName(rel
), colDef
->colname
),
7157 errdetail("\"%s\" versus \"%s\"",
7158 get_collation_name(ccollid
),
7159 get_collation_name(childatt
->attcollation
))));
7161 /* Bump the existing child att's inhcount */
7162 childatt
->attinhcount
++;
7163 if (childatt
->attinhcount
< 0)
7165 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7166 errmsg("too many inheritance parents"));
7167 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
7169 heap_freetuple(tuple
);
7171 /* Inform the user about the merge */
7173 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7174 colDef
->colname
, RelationGetRelationName(rel
))));
7176 table_close(attrdesc
, RowExclusiveLock
);
7178 /* Make the child column change visible */
7179 CommandCounterIncrement();
7181 return InvalidObjectAddress
;
7185 /* skip if the name already exists and if_not_exists is true */
7186 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
7188 table_close(attrdesc
, RowExclusiveLock
);
7189 return InvalidObjectAddress
;
7193 * Okay, we need to add the column, so go ahead and do parse
7194 * transformation. This can result in queueing up, or even immediately
7195 * executing, subsidiary operations (such as creation of unique indexes);
7196 * so we mustn't do it until we have made the if_not_exists check.
7198 * When recursing, the command was already transformed and we needn't do
7199 * so again. Also, if context isn't given we can't transform. (That
7200 * currently happens only for AT_AddColumnToView; we expect that view.c
7201 * passed us a ColumnDef that doesn't need work.)
7203 if (context
!= NULL
&& !recursing
)
7205 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
7207 Assert(*cmd
!= NULL
);
7208 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7212 * Regular inheritance children are independent enough not to inherit the
7213 * identity column from parent hence cannot recursively add identity
7214 * column if the table has inheritance children.
7216 * Partitions, on the other hand, are integral part of a partitioned table
7217 * and inherit identity column. Hence propagate identity column down the
7218 * partition hierarchy.
7220 if (colDef
->identity
&&
7222 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
&&
7223 find_inheritance_children(myrelid
, NoLock
) != NIL
)
7225 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7226 errmsg("cannot recursively add identity column to table that has child tables")));
7228 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
7230 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
7231 if (!HeapTupleIsValid(reltup
))
7232 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
7233 relkind
= ((Form_pg_class
) GETSTRUCT(reltup
))->relkind
;
7235 /* Determine the new attribute's number */
7236 newattnum
= ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
+ 1;
7237 if (newattnum
> MaxHeapAttributeNumber
)
7239 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
7240 errmsg("tables can have at most %d columns",
7241 MaxHeapAttributeNumber
)));
7244 * Construct new attribute's pg_attribute entry.
7246 tupdesc
= BuildDescForRelation(list_make1(colDef
));
7248 attribute
= TupleDescAttr(tupdesc
, 0);
7250 /* Fix up attribute number */
7251 attribute
->attnum
= newattnum
;
7253 /* make sure datatype is legal for a column */
7254 CheckAttributeType(NameStr(attribute
->attname
), attribute
->atttypid
, attribute
->attcollation
,
7255 list_make1_oid(rel
->rd_rel
->reltype
),
7258 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
7260 table_close(attrdesc
, RowExclusiveLock
);
7263 * Update pg_class tuple as appropriate
7265 ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
= newattnum
;
7267 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
7269 heap_freetuple(reltup
);
7271 /* Post creation hook for new attribute */
7272 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
7274 table_close(pgclass
, RowExclusiveLock
);
7276 /* Make the attribute's catalog entry visible */
7277 CommandCounterIncrement();
7280 * Store the DEFAULT, if any, in the catalogs
7282 if (colDef
->raw_default
)
7284 RawColumnDefault
*rawEnt
;
7286 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7287 rawEnt
->attnum
= attribute
->attnum
;
7288 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7291 * Attempt to skip a complete table rewrite by storing the specified
7292 * DEFAULT value outside of the heap. This may be disabled inside
7293 * AddRelationNewConstraints if the optimization cannot be applied.
7295 rawEnt
->missingMode
= (!colDef
->generated
);
7297 rawEnt
->generated
= colDef
->generated
;
7300 * This function is intended for CREATE TABLE, so it processes a
7301 * _list_ of defaults, but we just do one.
7303 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7304 false, true, false, NULL
);
7306 /* Make the additional catalog changes visible */
7307 CommandCounterIncrement();
7310 * Did the request for a missing value work? If not we'll have to do a
7313 if (!rawEnt
->missingMode
)
7314 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7318 * Tell Phase 3 to fill in the default expression, if there is one.
7320 * If there is no default, Phase 3 doesn't have to do anything, because
7321 * that effectively means that the default is NULL. The heap tuple access
7322 * routines always check for attnum > # of attributes in tuple, and return
7323 * NULL if so, so without any modification of the tuple data we will get
7324 * the effect of NULL values in the new column.
7326 * An exception occurs when the new column is of a domain type: the domain
7327 * might have a not-null constraint, or a check constraint that indirectly
7328 * rejects nulls. If there are any domain constraints then we construct
7329 * an explicit NULL default value that will be passed through
7330 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7331 * rewriting the table which we really don't have to do, but the present
7332 * design of domain processing doesn't offer any simple way of checking
7333 * the constraints more directly.)
7335 * Note: we use build_column_default, and not just the cooked default
7336 * returned by AddRelationNewConstraints, so that the right thing happens
7337 * when a datatype's default applies.
7339 * Note: it might seem that this should happen at the end of Phase 2, so
7340 * that the effects of subsequent subcommands can be taken into account.
7341 * It's intentional that we do it now, though. The new column should be
7342 * filled according to what is said in the ADD COLUMN subcommand, so that
7343 * the effects are the same as if this subcommand had been run by itself
7344 * and the later subcommands had been issued in new ALTER TABLE commands.
7346 * We can skip this entirely for relations without storage, since Phase 3
7347 * is certainly not going to touch them. System attributes don't have
7348 * interesting defaults, either.
7350 if (RELKIND_HAS_STORAGE(relkind
))
7353 * For an identity column, we can't use build_column_default(),
7354 * because the sequence ownership isn't set yet. So do it manually.
7356 if (colDef
->identity
)
7358 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7360 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7361 nve
->typeId
= attribute
->atttypid
;
7363 defval
= (Expr
*) nve
;
7365 /* must do a rewrite for identity columns */
7366 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7369 defval
= (Expr
*) build_column_default(rel
, attribute
->attnum
);
7371 if (!defval
&& DomainHasConstraints(attribute
->atttypid
))
7377 baseTypeMod
= attribute
->atttypmod
;
7378 baseTypeId
= getBaseTypeAndTypmod(attribute
->atttypid
, &baseTypeMod
);
7379 baseTypeColl
= get_typcollation(baseTypeId
);
7380 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7381 defval
= (Expr
*) coerce_to_target_type(NULL
,
7384 attribute
->atttypid
,
7385 attribute
->atttypmod
,
7386 COERCION_ASSIGNMENT
,
7387 COERCE_IMPLICIT_CAST
,
7389 if (defval
== NULL
) /* should not happen */
7390 elog(ERROR
, "failed to coerce base type to domain");
7395 NewColumnValue
*newval
;
7397 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7398 newval
->attnum
= attribute
->attnum
;
7399 newval
->expr
= expression_planner(defval
);
7400 newval
->is_generated
= (colDef
->generated
!= '\0');
7402 tab
->newvals
= lappend(tab
->newvals
, newval
);
7405 if (DomainHasConstraints(attribute
->atttypid
))
7406 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7408 if (!TupleDescAttr(rel
->rd_att
, attribute
->attnum
- 1)->atthasmissing
)
7411 * If the new column is NOT NULL, and there is no missing value,
7412 * tell Phase 3 it needs to check for NULLs.
7414 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7419 * Add needed dependency entries for the new column.
7421 add_column_datatype_dependency(myrelid
, newattnum
, attribute
->atttypid
);
7422 add_column_collation_dependency(myrelid
, newattnum
, attribute
->attcollation
);
7425 * Propagate to children as appropriate. Unlike most other ALTER
7426 * routines, we have to do this one level of recursion at a time; we can't
7427 * use find_all_inheritors to do it in one pass.
7430 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7433 * If we are told not to recurse, there had better not be any child
7434 * tables; else the addition would put them out of step.
7436 if (children
&& !recurse
)
7438 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7439 errmsg("column must be added to child tables too")));
7441 /* Children should see column as singly inherited */
7444 childcmd
= copyObject(*cmd
);
7445 colDef
= castNode(ColumnDef
, childcmd
->def
);
7446 colDef
->inhcount
= 1;
7447 colDef
->is_local
= false;
7450 childcmd
= *cmd
; /* no need to copy again */
7452 foreach(child
, children
)
7454 Oid childrelid
= lfirst_oid(child
);
7456 AlteredTableInfo
*childtab
;
7458 /* find_inheritance_children already got lock */
7459 childrel
= table_open(childrelid
, NoLock
);
7460 CheckTableNotInUse(childrel
, "ALTER TABLE");
7462 /* Find or create work queue entry for this table */
7463 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7465 /* Recurse to child; return value is ignored */
7466 ATExecAddColumn(wqueue
, childtab
, childrel
,
7467 &childcmd
, recurse
, true,
7468 lockmode
, cur_pass
, context
);
7470 table_close(childrel
, NoLock
);
7473 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7478 * If a new or renamed column will collide with the name of an existing
7479 * column and if_not_exists is false then error out, else do nothing.
7482 check_for_column_name_collision(Relation rel
, const char *colname
,
7489 * this test is deliberately not attisdropped-aware, since if one tries to
7490 * add a column matching a dropped column name, it's gonna fail anyway.
7492 attTuple
= SearchSysCache2(ATTNAME
,
7493 ObjectIdGetDatum(RelationGetRelid(rel
)),
7494 PointerGetDatum(colname
));
7495 if (!HeapTupleIsValid(attTuple
))
7498 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7499 ReleaseSysCache(attTuple
);
7502 * We throw a different error message for conflicts with system column
7503 * names, since they are normally not shown and the user might otherwise
7504 * be confused about the reason for the conflict.
7508 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7509 errmsg("column name \"%s\" conflicts with a system column name",
7516 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7517 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7518 colname
, RelationGetRelationName(rel
))));
7523 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7524 errmsg("column \"%s\" of relation \"%s\" already exists",
7525 colname
, RelationGetRelationName(rel
))));
7532 * Install a column's dependency on its datatype.
7535 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7537 ObjectAddress myself
,
7540 myself
.classId
= RelationRelationId
;
7541 myself
.objectId
= relid
;
7542 myself
.objectSubId
= attnum
;
7543 referenced
.classId
= TypeRelationId
;
7544 referenced
.objectId
= typid
;
7545 referenced
.objectSubId
= 0;
7546 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7550 * Install a column's dependency on its collation.
7553 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7555 ObjectAddress myself
,
7558 /* We know the default collation is pinned, so don't bother recording it */
7559 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7561 myself
.classId
= RelationRelationId
;
7562 myself
.objectId
= relid
;
7563 myself
.objectSubId
= attnum
;
7564 referenced
.classId
= CollationRelationId
;
7565 referenced
.objectId
= collid
;
7566 referenced
.objectSubId
= 0;
7567 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7572 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7574 * Return the address of the modified column. If the column was already
7575 * nullable, InvalidObjectAddress is returned.
7577 static ObjectAddress
7578 ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
7583 Form_pg_attribute attTup
;
7586 ObjectAddress address
;
7590 * lookup the attribute
7592 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7594 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7595 if (!HeapTupleIsValid(tuple
))
7597 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7598 errmsg("column \"%s\" of relation \"%s\" does not exist",
7599 colName
, RelationGetRelationName(rel
))));
7600 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7601 attnum
= attTup
->attnum
;
7602 ObjectAddressSubSet(address
, RelationRelationId
,
7603 RelationGetRelid(rel
), attnum
);
7605 /* If the column is already nullable there's nothing to do. */
7606 if (!attTup
->attnotnull
)
7608 table_close(attr_rel
, RowExclusiveLock
);
7609 return InvalidObjectAddress
;
7612 /* Prevent them from altering a system attribute */
7615 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7616 errmsg("cannot alter system column \"%s\"",
7619 if (attTup
->attidentity
)
7621 (errcode(ERRCODE_SYNTAX_ERROR
),
7622 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7623 colName
, RelationGetRelationName(rel
))));
7626 * It's not OK to remove a constraint only for the parent and leave it in
7627 * the children, so disallow that.
7631 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7633 PartitionDesc partdesc
;
7635 partdesc
= RelationGetPartitionDesc(rel
, true);
7637 if (partdesc
->nparts
> 0)
7639 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7640 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7641 errhint("Do not specify the ONLY keyword."));
7643 else if (rel
->rd_rel
->relhassubclass
&&
7644 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
7647 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7648 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7650 errhint("Do not specify the ONLY keyword."));
7655 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7657 if (rel
->rd_rel
->relispartition
)
7659 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7660 Relation parent
= table_open(parentId
, AccessShareLock
);
7661 TupleDesc tupDesc
= RelationGetDescr(parent
);
7662 AttrNumber parent_attnum
;
7664 parent_attnum
= get_attnum(parentId
, colName
);
7665 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7667 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7668 errmsg("column \"%s\" is marked NOT NULL in parent table",
7670 table_close(parent
, AccessShareLock
);
7674 * Find the constraint that makes this column NOT NULL, and drop it if we
7675 * see one. dropconstraint_internal() will do necessary consistency
7676 * checking. If there isn't one, there are two possibilities: either the
7677 * column is marked attnotnull because it's part of the primary key, and
7678 * then we just throw an appropriate error; or it's a leftover marking
7679 * that we can remove. However, before doing the latter, to avoid
7680 * breaking consistency any further, prevent this if the column is part of
7681 * the replica identity.
7683 conTup
= findNotNullConstraint(RelationGetRelid(rel
), colName
);
7690 * If the column is in a primary key, throw a specific error message.
7692 pkcols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_PRIMARY_KEY
);
7693 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
7696 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7697 errmsg("column \"%s\" is in a primary key", colName
));
7699 /* Also throw an error if the column is in the replica identity */
7700 ircols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_IDENTITY_KEY
);
7701 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
, ircols
))
7703 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7704 errmsg("column \"%s\" is in index used as replica identity",
7705 get_attname(RelationGetRelid(rel
), attnum
, false)));
7707 /* Otherwise, just remove the attnotnull marking and do nothing else. */
7708 attTup
->attnotnull
= false;
7709 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7713 /* The normal case: we have a pg_constraint row, remove it */
7715 dropconstraint_internal(rel
, conTup
, DROP_RESTRICT
, recurse
, false,
7716 false, &readyRels
, lockmode
);
7718 heap_freetuple(conTup
);
7721 InvokeObjectPostAlterHook(RelationRelationId
,
7722 RelationGetRelid(rel
), attnum
);
7724 table_close(attr_rel
, RowExclusiveLock
);
7730 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7731 * to verify it; recurses to apply the same to children.
7733 * When called to alter an existing table, 'wqueue' must be given so that we can
7734 * queue a check that existing tuples pass the constraint. When called from
7735 * table creation, 'wqueue' should be passed as NULL.
7737 * Returns true if the flag was set in any table, otherwise false.
7740 set_attnotnull(List
**wqueue
, Relation rel
, AttrNumber attnum
, bool recurse
,
7744 Form_pg_attribute attForm
;
7745 bool retval
= false;
7747 /* Guard against stack overflow due to overly deep inheritance tree. */
7748 check_stack_depth();
7750 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
7751 if (!HeapTupleIsValid(tuple
))
7752 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
7753 attnum
, RelationGetRelid(rel
));
7754 attForm
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7755 if (!attForm
->attnotnull
)
7759 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7761 attForm
->attnotnull
= true;
7762 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7764 table_close(attr_rel
, RowExclusiveLock
);
7767 * And set up for existing values to be checked, unless another
7768 * constraint already proves this.
7770 if (wqueue
&& !NotNullImpliedByRelConstraints(rel
, attForm
))
7772 AlteredTableInfo
*tab
;
7774 tab
= ATGetQueueEntry(wqueue
, rel
);
7775 tab
->verify_new_notnull
= true;
7786 /* Make above update visible, for multiple inheritance cases */
7788 CommandCounterIncrement();
7790 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7791 foreach(lc
, children
)
7793 Oid childrelid
= lfirst_oid(lc
);
7795 AttrNumber childattno
;
7797 /* find_inheritance_children already got lock */
7798 childrel
= table_open(childrelid
, NoLock
);
7799 CheckTableNotInUse(childrel
, "ALTER TABLE");
7801 childattno
= get_attnum(RelationGetRelid(childrel
),
7802 get_attname(RelationGetRelid(rel
), attnum
,
7804 retval
|= set_attnotnull(wqueue
, childrel
, childattno
,
7806 table_close(childrel
, NoLock
);
7814 * ALTER TABLE ALTER COLUMN SET NOT NULL
7816 * Add a not-null constraint to a single table and its children. Returns
7817 * the address of the constraint added to the parent relation, if one gets
7818 * added, or InvalidObjectAddress otherwise.
7820 * We must recurse to child tables during execution, rather than using
7821 * ALTER TABLE's normal prep-time recursion.
7823 static ObjectAddress
7824 ATExecSetNotNull(List
**wqueue
, Relation rel
, char *conName
, char *colName
,
7825 bool recurse
, bool recursing
, List
**readyRels
,
7829 Relation constr_rel
;
7831 SysScanDesc conscan
;
7833 ObjectAddress address
;
7834 Constraint
*constraint
;
7835 CookedConstraint
*ccon
;
7837 bool is_no_inherit
= false;
7840 /* Guard against stack overflow due to overly deep inheritance tree. */
7841 check_stack_depth();
7844 * In cases of multiple inheritance, we might visit the same child more
7845 * than once. In the topmost call, set up a list that we fill with all
7846 * visited relations, to skip those.
7848 if (readyRels
== NULL
)
7853 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
7854 return InvalidObjectAddress
;
7855 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
7857 /* At top level, permission check was done in ATPrepCmd, else do it */
7860 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7861 Assert(conName
!= NULL
);
7864 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7865 if (attnum
== InvalidAttrNumber
)
7867 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7868 errmsg("column \"%s\" of relation \"%s\" does not exist",
7869 colName
, RelationGetRelationName(rel
))));
7871 /* Prevent them from altering a system attribute */
7874 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7875 errmsg("cannot alter system column \"%s\"",
7878 /* See if there's already a constraint */
7879 constr_rel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
7881 Anum_pg_constraint_conrelid
,
7882 BTEqualStrategyNumber
, F_OIDEQ
,
7883 ObjectIdGetDatum(RelationGetRelid(rel
)));
7884 conscan
= systable_beginscan(constr_rel
, ConstraintRelidTypidNameIndexId
, true,
7887 while (HeapTupleIsValid(tuple
= systable_getnext(conscan
)))
7889 Form_pg_constraint conForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
7890 bool changed
= false;
7893 if (conForm
->contype
!= CONSTRAINT_NOTNULL
)
7896 if (extractNotNullColumn(tuple
) != attnum
)
7899 copytup
= heap_copytuple(tuple
);
7900 conForm
= (Form_pg_constraint
) GETSTRUCT(copytup
);
7903 * Don't let a NO INHERIT constraint be changed into inherit.
7905 if (conForm
->connoinherit
&& recurse
)
7907 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7908 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7909 NameStr(conForm
->conname
),
7910 RelationGetRelationName(rel
)));
7913 * If we find an appropriate constraint, we're almost done, but just
7914 * need to change some properties on it: if we're recursing, increment
7915 * coninhcount; if not, set conislocal if not already set.
7919 conForm
->coninhcount
++;
7922 else if (!conForm
->conislocal
)
7924 conForm
->conislocal
= true;
7930 CatalogTupleUpdate(constr_rel
, ©tup
->t_self
, copytup
);
7931 ObjectAddressSet(address
, ConstraintRelationId
, conForm
->oid
);
7934 systable_endscan(conscan
);
7935 table_close(constr_rel
, RowExclusiveLock
);
7940 return InvalidObjectAddress
;
7943 systable_endscan(conscan
);
7944 table_close(constr_rel
, RowExclusiveLock
);
7947 * If we're asked not to recurse, and children exist, raise an error for
7948 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7952 find_inheritance_children(RelationGetRelid(rel
),
7955 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7957 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7958 errmsg("constraint must be added to child tables too"),
7959 errhint("Do not specify the ONLY keyword."));
7961 is_no_inherit
= true;
7965 * No constraint exists; we must add one. First determine a name to use,
7966 * if we haven't already.
7970 Assert(conName
== NULL
);
7971 conName
= ChooseConstraintName(RelationGetRelationName(rel
),
7972 colName
, "not_null",
7973 RelationGetNamespace(rel
),
7976 constraint
= makeNode(Constraint
);
7977 constraint
->contype
= CONSTR_NOTNULL
;
7978 constraint
->conname
= conName
;
7979 constraint
->deferrable
= false;
7980 constraint
->initdeferred
= false;
7981 constraint
->location
= -1;
7982 constraint
->keys
= list_make1(makeString(colName
));
7983 constraint
->is_no_inherit
= is_no_inherit
;
7984 constraint
->inhcount
= recursing
? 1 : 0;
7985 constraint
->skip_validation
= false;
7986 constraint
->initially_valid
= true;
7989 cooked
= AddRelationNewConstraints(rel
, NIL
, list_make1(constraint
),
7990 false, !recursing
, false, NULL
);
7991 ccon
= linitial(cooked
);
7992 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
7994 InvokeObjectPostAlterHook(RelationRelationId
,
7995 RelationGetRelid(rel
), attnum
);
7998 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7999 * recurse, because we're going to do it here.
8001 set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
);
8004 * Recurse to propagate the constraint to children that don't have one.
8011 children
= find_inheritance_children(RelationGetRelid(rel
),
8014 foreach(lc
, children
)
8018 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8020 ATExecSetNotNull(wqueue
, childrel
,
8021 conName
, colName
, recurse
, true,
8022 readyRels
, lockmode
);
8024 table_close(childrel
, NoLock
);
8032 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
8034 * This doesn't exist in the grammar; it's used when creating a
8035 * primary key and the column is not already marked attnotnull.
8037 static ObjectAddress
8038 ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
8039 const char *colName
, LOCKMODE lockmode
)
8042 ObjectAddress address
= InvalidObjectAddress
;
8044 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
8045 if (attnum
== InvalidAttrNumber
)
8047 errcode(ERRCODE_UNDEFINED_COLUMN
),
8048 errmsg("column \"%s\" of relation \"%s\" does not exist",
8049 colName
, RelationGetRelationName(rel
)));
8052 * Make the change, if necessary, and only if so report the column as
8055 if (set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
))
8056 ObjectAddressSubSet(address
, RelationRelationId
,
8057 RelationGetRelid(rel
), attnum
);
8063 * NotNullImpliedByRelConstraints
8064 * Does rel's existing constraints imply NOT NULL for the given attribute?
8067 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
8069 NullTest
*nnulltest
= makeNode(NullTest
);
8071 nnulltest
->arg
= (Expr
*) makeVar(1,
8077 nnulltest
->nulltesttype
= IS_NOT_NULL
;
8080 * argisrow = false is correct even for a composite column, because
8081 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8082 * case, just IS DISTINCT FROM NULL.
8084 nnulltest
->argisrow
= false;
8085 nnulltest
->location
= -1;
8087 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
8090 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8091 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
8099 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8101 * Return the address of the affected column.
8103 static ObjectAddress
8104 ATExecColumnDefault(Relation rel
, const char *colName
,
8105 Node
*newDefault
, LOCKMODE lockmode
)
8107 TupleDesc tupdesc
= RelationGetDescr(rel
);
8109 ObjectAddress address
;
8112 * get the number of the attribute
8114 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
8115 if (attnum
== InvalidAttrNumber
)
8117 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8118 errmsg("column \"%s\" of relation \"%s\" does not exist",
8119 colName
, RelationGetRelationName(rel
))));
8121 /* Prevent them from altering a system attribute */
8124 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8125 errmsg("cannot alter system column \"%s\"",
8128 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
8130 (errcode(ERRCODE_SYNTAX_ERROR
),
8131 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8132 colName
, RelationGetRelationName(rel
)),
8133 /* translator: %s is an SQL ALTER command */
8134 newDefault
? 0 : errhint("Use %s instead.",
8135 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8137 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
8139 (errcode(ERRCODE_SYNTAX_ERROR
),
8140 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8141 colName
, RelationGetRelationName(rel
)),
8143 /* translator: %s is an SQL ALTER command */
8144 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8145 (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
== ATTRIBUTE_GENERATED_STORED
?
8146 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8149 * Remove any old default for the column. We use RESTRICT here for
8150 * safety, but at present we do not expect anything to depend on the
8153 * We treat removing the existing default as an internal operation when it
8154 * is preparatory to adding a new default, but as a user-initiated
8155 * operation when the user asked for a drop.
8157 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8158 newDefault
!= NULL
);
8163 RawColumnDefault
*rawEnt
;
8165 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8166 rawEnt
->attnum
= attnum
;
8167 rawEnt
->raw_default
= newDefault
;
8168 rawEnt
->missingMode
= false;
8169 rawEnt
->generated
= '\0';
8172 * This function is intended for CREATE TABLE, so it processes a
8173 * _list_ of defaults, but we just do one.
8175 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8176 false, true, false, NULL
);
8179 ObjectAddressSubSet(address
, RelationRelationId
,
8180 RelationGetRelid(rel
), attnum
);
8185 * Add a pre-cooked default expression.
8187 * Return the address of the affected column.
8189 static ObjectAddress
8190 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
8193 ObjectAddress address
;
8195 /* We assume no checking is required */
8198 * Remove any old default for the column. We use RESTRICT here for
8199 * safety, but at present we do not expect anything to depend on the
8200 * default. (In ordinary cases, there could not be a default in place
8201 * anyway, but it's possible when combining LIKE with inheritance.)
8203 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8206 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
8208 ObjectAddressSubSet(address
, RelationRelationId
,
8209 RelationGetRelid(rel
), attnum
);
8214 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8216 * Return the address of the affected column.
8218 static ObjectAddress
8219 ATExecAddIdentity(Relation rel
, const char *colName
,
8220 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
)
8222 Relation attrelation
;
8224 Form_pg_attribute attTup
;
8226 ObjectAddress address
;
8227 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
8230 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8231 if (ispartitioned
&& !recurse
)
8233 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8234 errmsg("cannot add identity to a column of only the partitioned table"),
8235 errhint("Do not specify the ONLY keyword.")));
8237 if (rel
->rd_rel
->relispartition
&& !recursing
)
8239 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8240 errmsg("cannot add identity to a column of a partition"));
8242 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8244 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8245 if (!HeapTupleIsValid(tuple
))
8247 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8248 errmsg("column \"%s\" of relation \"%s\" does not exist",
8249 colName
, RelationGetRelationName(rel
))));
8250 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8251 attnum
= attTup
->attnum
;
8253 /* Can't alter a system attribute */
8256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8257 errmsg("cannot alter system column \"%s\"",
8261 * Creating a column as identity implies NOT NULL, so adding the identity
8262 * to an existing column that is not NOT NULL would create a state that
8263 * cannot be reproduced without contortions.
8265 if (!attTup
->attnotnull
)
8267 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8268 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8269 colName
, RelationGetRelationName(rel
))));
8271 if (attTup
->attidentity
)
8273 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8274 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8275 colName
, RelationGetRelationName(rel
))));
8277 if (attTup
->atthasdef
)
8279 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8280 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8281 colName
, RelationGetRelationName(rel
))));
8283 attTup
->attidentity
= cdef
->identity
;
8284 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8286 InvokeObjectPostAlterHook(RelationRelationId
,
8287 RelationGetRelid(rel
),
8289 ObjectAddressSubSet(address
, RelationRelationId
,
8290 RelationGetRelid(rel
), attnum
);
8291 heap_freetuple(tuple
);
8293 table_close(attrelation
, RowExclusiveLock
);
8296 * Recurse to propagate the identity column to partitions. Identity is
8297 * not inherited in regular inheritance children.
8299 if (recurse
&& ispartitioned
)
8304 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8306 foreach(lc
, children
)
8310 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8311 ATExecAddIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8312 table_close(childrel
, NoLock
);
8320 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8322 * Return the address of the affected column.
8324 static ObjectAddress
8325 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
,
8326 LOCKMODE lockmode
, bool recurse
, bool recursing
)
8329 DefElem
*generatedEl
= NULL
;
8331 Form_pg_attribute attTup
;
8333 Relation attrelation
;
8334 ObjectAddress address
;
8337 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8338 if (ispartitioned
&& !recurse
)
8340 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8341 errmsg("cannot change identity column of only the partitioned table"),
8342 errhint("Do not specify the ONLY keyword.")));
8344 if (rel
->rd_rel
->relispartition
&& !recursing
)
8346 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8347 errmsg("cannot change identity column of a partition"));
8349 foreach(option
, castNode(List
, def
))
8351 DefElem
*defel
= lfirst_node(DefElem
, option
);
8353 if (strcmp(defel
->defname
, "generated") == 0)
8357 (errcode(ERRCODE_SYNTAX_ERROR
),
8358 errmsg("conflicting or redundant options")));
8359 generatedEl
= defel
;
8362 elog(ERROR
, "option \"%s\" not recognized",
8367 * Even if there is nothing to change here, we run all the checks. There
8368 * will be a subsequent ALTER SEQUENCE that relies on everything being
8372 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8373 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8374 if (!HeapTupleIsValid(tuple
))
8376 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8377 errmsg("column \"%s\" of relation \"%s\" does not exist",
8378 colName
, RelationGetRelationName(rel
))));
8380 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8381 attnum
= attTup
->attnum
;
8385 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8386 errmsg("cannot alter system column \"%s\"",
8389 if (!attTup
->attidentity
)
8391 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8392 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8393 colName
, RelationGetRelationName(rel
))));
8397 attTup
->attidentity
= defGetInt32(generatedEl
);
8398 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8400 InvokeObjectPostAlterHook(RelationRelationId
,
8401 RelationGetRelid(rel
),
8403 ObjectAddressSubSet(address
, RelationRelationId
,
8404 RelationGetRelid(rel
), attnum
);
8407 address
= InvalidObjectAddress
;
8409 heap_freetuple(tuple
);
8410 table_close(attrelation
, RowExclusiveLock
);
8413 * Recurse to propagate the identity change to partitions. Identity is not
8414 * inherited in regular inheritance children.
8416 if (generatedEl
&& recurse
&& ispartitioned
)
8421 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8423 foreach(lc
, children
)
8427 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8428 ATExecSetIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8429 table_close(childrel
, NoLock
);
8437 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8439 * Return the address of the affected column.
8441 static ObjectAddress
8442 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
8443 bool recurse
, bool recursing
)
8446 Form_pg_attribute attTup
;
8448 Relation attrelation
;
8449 ObjectAddress address
;
8451 ObjectAddress seqaddress
;
8454 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8455 if (ispartitioned
&& !recurse
)
8457 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8458 errmsg("cannot drop identity from a column of only the partitioned table"),
8459 errhint("Do not specify the ONLY keyword.")));
8461 if (rel
->rd_rel
->relispartition
&& !recursing
)
8463 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8464 errmsg("cannot drop identity from a column of a partition"));
8466 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8467 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8468 if (!HeapTupleIsValid(tuple
))
8470 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8471 errmsg("column \"%s\" of relation \"%s\" does not exist",
8472 colName
, RelationGetRelationName(rel
))));
8474 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8475 attnum
= attTup
->attnum
;
8479 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8480 errmsg("cannot alter system column \"%s\"",
8483 if (!attTup
->attidentity
)
8487 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8488 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8489 colName
, RelationGetRelationName(rel
))));
8493 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8494 colName
, RelationGetRelationName(rel
))));
8495 heap_freetuple(tuple
);
8496 table_close(attrelation
, RowExclusiveLock
);
8497 return InvalidObjectAddress
;
8501 attTup
->attidentity
= '\0';
8502 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8504 InvokeObjectPostAlterHook(RelationRelationId
,
8505 RelationGetRelid(rel
),
8507 ObjectAddressSubSet(address
, RelationRelationId
,
8508 RelationGetRelid(rel
), attnum
);
8509 heap_freetuple(tuple
);
8511 table_close(attrelation
, RowExclusiveLock
);
8514 * Recurse to drop the identity from column in partitions. Identity is
8515 * not inherited in regular inheritance children so ignore them.
8517 if (recurse
&& ispartitioned
)
8522 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8524 foreach(lc
, children
)
8528 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8529 ATExecDropIdentity(childrel
, colName
, false, lockmode
, recurse
, true);
8530 table_close(childrel
, NoLock
);
8536 /* drop the internal sequence */
8537 seqid
= getIdentitySequence(rel
, attnum
, false);
8538 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
8539 RelationRelationId
, DEPENDENCY_INTERNAL
);
8540 CommandCounterIncrement();
8541 seqaddress
.classId
= RelationRelationId
;
8542 seqaddress
.objectId
= seqid
;
8543 seqaddress
.objectSubId
= 0;
8544 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8551 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8553 * Return the address of the affected column.
8555 static ObjectAddress
8556 ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
8557 Node
*newExpr
, LOCKMODE lockmode
)
8560 Form_pg_attribute attTup
;
8563 ObjectAddress address
;
8565 NewColumnValue
*newval
;
8566 RawColumnDefault
*rawEnt
;
8568 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8569 if (!HeapTupleIsValid(tuple
))
8571 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8572 errmsg("column \"%s\" of relation \"%s\" does not exist",
8573 colName
, RelationGetRelationName(rel
))));
8575 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8576 attnum
= attTup
->attnum
;
8580 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8581 errmsg("cannot alter system column \"%s\"",
8584 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8586 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8587 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8588 colName
, RelationGetRelationName(rel
))));
8589 ReleaseSysCache(tuple
);
8592 * Clear all the missing values if we're rewriting the table, since this
8593 * renders them pointless.
8595 RelationClearMissing(rel
);
8597 /* make sure we don't conflict with later attribute modifications */
8598 CommandCounterIncrement();
8601 * Find everything that depends on the column (constraints, indexes, etc),
8602 * and record enough information to let us recreate the objects after
8605 RememberAllDependentForRebuilding(tab
, AT_SetExpression
, rel
, attnum
, colName
);
8608 * Drop the dependency records of the GENERATED expression, in particular
8609 * its INTERNAL dependency on the column, which would otherwise cause
8610 * dependency.c to refuse to perform the deletion.
8612 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8613 if (!OidIsValid(attrdefoid
))
8614 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8615 RelationGetRelid(rel
), attnum
);
8616 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8618 /* Make above changes visible */
8619 CommandCounterIncrement();
8622 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8623 * safety, but at present we do not expect anything to depend on the
8626 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8629 /* Prepare to store the new expression, in the catalogs */
8630 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8631 rawEnt
->attnum
= attnum
;
8632 rawEnt
->raw_default
= newExpr
;
8633 rawEnt
->missingMode
= false;
8634 rawEnt
->generated
= ATTRIBUTE_GENERATED_STORED
;
8636 /* Store the generated expression */
8637 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8638 false, true, false, NULL
);
8640 /* Make above new expression visible */
8641 CommandCounterIncrement();
8643 /* Prepare for table rewrite */
8644 defval
= (Expr
*) build_column_default(rel
, attnum
);
8646 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
8647 newval
->attnum
= attnum
;
8648 newval
->expr
= expression_planner(defval
);
8649 newval
->is_generated
= true;
8651 tab
->newvals
= lappend(tab
->newvals
, newval
);
8652 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
8654 /* Drop any pg_statistic entry for the column */
8655 RemoveStatistics(RelationGetRelid(rel
), attnum
);
8657 InvokeObjectPostAlterHook(RelationRelationId
,
8658 RelationGetRelid(rel
), attnum
);
8660 ObjectAddressSubSet(address
, RelationRelationId
,
8661 RelationGetRelid(rel
), attnum
);
8666 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8669 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8672 * Reject ONLY if there are child tables. We could implement this, but it
8673 * is a bit complicated. GENERATED clauses must be attached to the column
8674 * definition and cannot be added later like DEFAULT, so if a child table
8675 * has a generation expression that the parent does not have, the child
8676 * column will necessarily be an attislocal column. So to implement ONLY
8677 * here, we'd need extra code to update attislocal of the direct child
8678 * tables, somewhat similar to how DROP COLUMN does it, so that the
8679 * resulting state can be properly dumped and restored.
8682 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8684 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8685 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8688 * Cannot drop generation expression from inherited columns.
8693 Form_pg_attribute attTup
;
8695 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8696 if (!HeapTupleIsValid(tuple
))
8698 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8699 errmsg("column \"%s\" of relation \"%s\" does not exist",
8700 cmd
->name
, RelationGetRelationName(rel
))));
8702 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8704 if (attTup
->attinhcount
> 0)
8706 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8707 errmsg("cannot drop generation expression from inherited column")));
8712 * Return the address of the affected column.
8714 static ObjectAddress
8715 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8718 Form_pg_attribute attTup
;
8720 Relation attrelation
;
8722 ObjectAddress address
;
8724 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8725 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8726 if (!HeapTupleIsValid(tuple
))
8728 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8729 errmsg("column \"%s\" of relation \"%s\" does not exist",
8730 colName
, RelationGetRelationName(rel
))));
8732 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8733 attnum
= attTup
->attnum
;
8737 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8738 errmsg("cannot alter system column \"%s\"",
8741 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8745 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8746 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8747 colName
, RelationGetRelationName(rel
))));
8751 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8752 colName
, RelationGetRelationName(rel
))));
8753 heap_freetuple(tuple
);
8754 table_close(attrelation
, RowExclusiveLock
);
8755 return InvalidObjectAddress
;
8760 * Mark the column as no longer generated. (The atthasdef flag needs to
8761 * get cleared too, but RemoveAttrDefault will handle that.)
8763 attTup
->attgenerated
= '\0';
8764 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8766 InvokeObjectPostAlterHook(RelationRelationId
,
8767 RelationGetRelid(rel
),
8769 heap_freetuple(tuple
);
8771 table_close(attrelation
, RowExclusiveLock
);
8774 * Drop the dependency records of the GENERATED expression, in particular
8775 * its INTERNAL dependency on the column, which would otherwise cause
8776 * dependency.c to refuse to perform the deletion.
8778 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8779 if (!OidIsValid(attrdefoid
))
8780 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8781 RelationGetRelid(rel
), attnum
);
8782 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8784 /* Make above changes visible */
8785 CommandCounterIncrement();
8788 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8789 * safety, but at present we do not expect anything to depend on the
8792 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8795 ObjectAddressSubSet(address
, RelationRelationId
,
8796 RelationGetRelid(rel
), attnum
);
8801 * ALTER TABLE ALTER COLUMN SET STATISTICS
8803 * Return value is the address of the modified column
8805 static ObjectAddress
8806 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8809 bool newtarget_default
;
8810 Relation attrelation
;
8813 Form_pg_attribute attrtuple
;
8815 ObjectAddress address
;
8816 Datum repl_val
[Natts_pg_attribute
];
8817 bool repl_null
[Natts_pg_attribute
];
8818 bool repl_repl
[Natts_pg_attribute
];
8821 * We allow referencing columns by numbers only for indexes, since table
8822 * column numbers could contain gaps if columns are later dropped.
8824 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8825 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8828 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8829 errmsg("cannot refer to non-index column by number")));
8831 /* -1 was used in previous versions for the default setting */
8832 if (newValue
&& intVal(newValue
) != -1)
8834 newtarget
= intVal(newValue
);
8835 newtarget_default
= false;
8838 newtarget_default
= true;
8840 if (!newtarget_default
)
8843 * Limit target to a sane range
8848 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8849 errmsg("statistics target %d is too low",
8852 else if (newtarget
> MAX_STATISTICS_TARGET
)
8854 newtarget
= MAX_STATISTICS_TARGET
;
8856 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8857 errmsg("lowering statistics target to %d",
8862 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8866 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8868 if (!HeapTupleIsValid(tuple
))
8870 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8871 errmsg("column \"%s\" of relation \"%s\" does not exist",
8872 colName
, RelationGetRelationName(rel
))));
8876 tuple
= SearchSysCacheAttNum(RelationGetRelid(rel
), colNum
);
8878 if (!HeapTupleIsValid(tuple
))
8880 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8881 errmsg("column number %d of relation \"%s\" does not exist",
8882 colNum
, RelationGetRelationName(rel
))));
8885 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8887 attnum
= attrtuple
->attnum
;
8890 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8891 errmsg("cannot alter system column \"%s\"",
8894 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8895 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8897 if (attnum
> rel
->rd_index
->indnkeyatts
)
8899 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8900 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8901 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8902 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8904 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8905 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8906 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8907 errhint("Alter statistics on table column instead.")));
8910 /* Build new tuple. */
8911 memset(repl_null
, false, sizeof(repl_null
));
8912 memset(repl_repl
, false, sizeof(repl_repl
));
8913 if (!newtarget_default
)
8914 repl_val
[Anum_pg_attribute_attstattarget
- 1] = newtarget
;
8916 repl_null
[Anum_pg_attribute_attstattarget
- 1] = true;
8917 repl_repl
[Anum_pg_attribute_attstattarget
- 1] = true;
8918 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8919 repl_val
, repl_null
, repl_repl
);
8920 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, newtuple
);
8922 InvokeObjectPostAlterHook(RelationRelationId
,
8923 RelationGetRelid(rel
),
8925 ObjectAddressSubSet(address
, RelationRelationId
,
8926 RelationGetRelid(rel
), attnum
);
8928 heap_freetuple(newtuple
);
8930 ReleaseSysCache(tuple
);
8932 table_close(attrelation
, RowExclusiveLock
);
8938 * Return value is the address of the modified column
8940 static ObjectAddress
8941 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8942 bool isReset
, LOCKMODE lockmode
)
8944 Relation attrelation
;
8947 Form_pg_attribute attrtuple
;
8952 ObjectAddress address
;
8953 Datum repl_val
[Natts_pg_attribute
];
8954 bool repl_null
[Natts_pg_attribute
];
8955 bool repl_repl
[Natts_pg_attribute
];
8957 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8959 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8961 if (!HeapTupleIsValid(tuple
))
8963 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8964 errmsg("column \"%s\" of relation \"%s\" does not exist",
8965 colName
, RelationGetRelationName(rel
))));
8966 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8968 attnum
= attrtuple
->attnum
;
8971 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8972 errmsg("cannot alter system column \"%s\"",
8975 /* Generate new proposed attoptions (text array) */
8976 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8978 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8979 castNode(List
, options
), NULL
, NULL
,
8981 /* Validate new options */
8982 (void) attribute_reloptions(newOptions
, true);
8984 /* Build new tuple. */
8985 memset(repl_null
, false, sizeof(repl_null
));
8986 memset(repl_repl
, false, sizeof(repl_repl
));
8987 if (newOptions
!= (Datum
) 0)
8988 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8990 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8991 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8992 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8993 repl_val
, repl_null
, repl_repl
);
8995 /* Update system catalog. */
8996 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8998 InvokeObjectPostAlterHook(RelationRelationId
,
8999 RelationGetRelid(rel
),
9001 ObjectAddressSubSet(address
, RelationRelationId
,
9002 RelationGetRelid(rel
), attnum
);
9004 heap_freetuple(newtuple
);
9006 ReleaseSysCache(tuple
);
9008 table_close(attrelation
, RowExclusiveLock
);
9014 * Helper function for ATExecSetStorage and ATExecSetCompression
9016 * Set the attstorage and/or attcompression fields for index columns
9017 * associated with the specified table column.
9020 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
9022 bool setstorage
, char newstorage
,
9023 bool setcompression
, char newcompression
,
9028 foreach(lc
, RelationGetIndexList(rel
))
9030 Oid indexoid
= lfirst_oid(lc
);
9032 AttrNumber indattnum
= 0;
9035 indrel
= index_open(indexoid
, lockmode
);
9037 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
9039 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
9048 index_close(indrel
, lockmode
);
9052 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
9054 if (HeapTupleIsValid(tuple
))
9056 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9059 attrtuple
->attstorage
= newstorage
;
9062 attrtuple
->attcompression
= newcompression
;
9064 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
9066 InvokeObjectPostAlterHook(RelationRelationId
,
9067 RelationGetRelid(rel
),
9070 heap_freetuple(tuple
);
9073 index_close(indrel
, lockmode
);
9078 * ALTER TABLE ALTER COLUMN SET STORAGE
9080 * Return value is the address of the modified column
9082 static ObjectAddress
9083 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
9085 Relation attrelation
;
9087 Form_pg_attribute attrtuple
;
9089 ObjectAddress address
;
9091 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
9093 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
9095 if (!HeapTupleIsValid(tuple
))
9097 (errcode(ERRCODE_UNDEFINED_COLUMN
),
9098 errmsg("column \"%s\" of relation \"%s\" does not exist",
9099 colName
, RelationGetRelationName(rel
))));
9100 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9102 attnum
= attrtuple
->attnum
;
9105 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9106 errmsg("cannot alter system column \"%s\"",
9109 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
9111 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
9113 InvokeObjectPostAlterHook(RelationRelationId
,
9114 RelationGetRelid(rel
),
9118 * Apply the change to indexes as well (only for simple index columns,
9119 * matching behavior of index.c ConstructTupleDescriptor()).
9121 SetIndexStorageProperties(rel
, attrelation
, attnum
,
9122 true, attrtuple
->attstorage
,
9126 heap_freetuple(tuple
);
9128 table_close(attrelation
, RowExclusiveLock
);
9130 ObjectAddressSubSet(address
, RelationRelationId
,
9131 RelationGetRelid(rel
), attnum
);
9137 * ALTER TABLE DROP COLUMN
9139 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9140 * because we have to decide at runtime whether to recurse or not depending
9141 * on whether attinhcount goes to zero or not. (We can't check this in a
9142 * static pre-pass because it won't handle multiple inheritance situations
9146 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
9147 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
9148 AlterTableUtilityContext
*context
)
9150 if (rel
->rd_rel
->reloftype
&& !recursing
)
9152 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9153 errmsg("cannot drop column from typed table")));
9155 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
9156 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
9159 cmd
->recurse
= true;
9163 * Drops column 'colName' from relation 'rel' and returns the address of the
9164 * dropped column. The column is also dropped (or marked as no longer
9165 * inherited from relation) from the relation's inheritance children, if any.
9167 * In the recursive invocations for inheritance child relations, instead of
9168 * dropping the column directly (if to be dropped at all), its object address
9169 * is added to 'addrs', which must be non-NULL in such invocations. All
9170 * columns are dropped at the same time after all the children have been
9171 * checked recursively.
9173 static ObjectAddress
9174 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
9175 DropBehavior behavior
,
9176 bool recurse
, bool recursing
,
9177 bool missing_ok
, LOCKMODE lockmode
,
9178 ObjectAddresses
*addrs
)
9181 Form_pg_attribute targetatt
;
9184 ObjectAddress object
;
9187 /* At top level, permission check was done in ATPrepCmd, else do it */
9189 ATSimplePermissions(AT_DropColumn
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9191 /* Initialize addrs on the first invocation */
9192 Assert(!recursing
|| addrs
!= NULL
);
9194 /* since this function recurses, it could be driven to stack overflow */
9195 check_stack_depth();
9198 addrs
= new_object_addresses();
9201 * get the number of the attribute
9203 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
9204 if (!HeapTupleIsValid(tuple
))
9209 (errcode(ERRCODE_UNDEFINED_COLUMN
),
9210 errmsg("column \"%s\" of relation \"%s\" does not exist",
9211 colName
, RelationGetRelationName(rel
))));
9216 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9217 colName
, RelationGetRelationName(rel
))));
9218 return InvalidObjectAddress
;
9221 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9223 attnum
= targetatt
->attnum
;
9225 /* Can't drop a system attribute */
9228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9229 errmsg("cannot drop system column \"%s\"",
9233 * Don't drop inherited columns, unless recursing (presumably from a drop
9234 * of the parent column)
9236 if (targetatt
->attinhcount
> 0 && !recursing
)
9238 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9239 errmsg("cannot drop inherited column \"%s\"",
9243 * Don't drop columns used in the partition key, either. (If we let this
9244 * go through, the key column's dependencies would cause a cascaded drop
9245 * of the whole table, which is surely not what the user expected.)
9247 if (has_partition_attrs(rel
,
9248 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
9251 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9252 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9253 colName
, RelationGetRelationName(rel
))));
9255 ReleaseSysCache(tuple
);
9258 * Propagate to children as appropriate. Unlike most other ALTER
9259 * routines, we have to do this one level of recursion at a time; we can't
9260 * use find_all_inheritors to do it in one pass.
9263 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9271 * In case of a partitioned table, the column must be dropped from the
9272 * partitions as well.
9274 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
9276 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9277 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9278 errhint("Do not specify the ONLY keyword.")));
9280 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
9281 foreach(child
, children
)
9283 Oid childrelid
= lfirst_oid(child
);
9285 Form_pg_attribute childatt
;
9287 /* find_inheritance_children already got lock */
9288 childrel
= table_open(childrelid
, NoLock
);
9289 CheckTableNotInUse(childrel
, "ALTER TABLE");
9291 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
9292 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
9293 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
9294 colName
, childrelid
);
9295 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9297 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
9298 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
9299 childrelid
, colName
);
9304 * If the child column has other definition sources, just
9305 * decrement its inheritance count; if not, recurse to delete
9308 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
9310 /* Time to delete this child column, too */
9311 ATExecDropColumn(wqueue
, childrel
, colName
,
9312 behavior
, true, true,
9313 false, lockmode
, addrs
);
9317 /* Child column must survive my deletion */
9318 childatt
->attinhcount
--;
9320 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9322 /* Make update visible */
9323 CommandCounterIncrement();
9329 * If we were told to drop ONLY in this table (no recursion),
9330 * we need to mark the inheritors' attributes as locally
9331 * defined rather than inherited.
9333 childatt
->attinhcount
--;
9334 childatt
->attislocal
= true;
9336 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9338 /* Make update visible */
9339 CommandCounterIncrement();
9342 heap_freetuple(tuple
);
9344 table_close(childrel
, NoLock
);
9346 table_close(attr_rel
, RowExclusiveLock
);
9349 /* Add object to delete */
9350 object
.classId
= RelationRelationId
;
9351 object
.objectId
= RelationGetRelid(rel
);
9352 object
.objectSubId
= attnum
;
9353 add_exact_object_address(&object
, addrs
);
9357 /* Recursion has ended, drop everything that was collected */
9358 performMultipleDeletions(addrs
, behavior
, 0);
9359 free_object_addresses(addrs
);
9366 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9367 * constraint on its children.
9370 ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
9371 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
9374 List
*newconstrs
= NIL
;
9375 IndexStmt
*indexstmt
;
9377 /* No work if not creating a primary key */
9378 if (!IsA(cmd
->def
, IndexStmt
))
9380 indexstmt
= castNode(IndexStmt
, cmd
->def
);
9381 if (!indexstmt
->primary
)
9384 /* No work if no legacy inheritance children are present */
9385 if (rel
->rd_rel
->relkind
!= RELKIND_RELATION
||
9386 !rel
->rd_rel
->relhassubclass
)
9390 * Acquire locks all the way down the hierarchy. The recursion to lower
9391 * levels occurs at execution time as necessary, so we don't need to do it
9392 * here, and we don't need the returned list either.
9394 (void) find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
9397 * Construct the list of constraints that we need to add to each child
9400 foreach_node(IndexElem
, elem
, indexstmt
->indexParams
)
9402 Constraint
*nnconstr
;
9404 Assert(elem
->expr
== NULL
);
9406 nnconstr
= makeNode(Constraint
);
9407 nnconstr
->contype
= CONSTR_NOTNULL
;
9408 nnconstr
->conname
= NULL
; /* XXX use PK name? */
9409 nnconstr
->inhcount
= 1;
9410 nnconstr
->deferrable
= false;
9411 nnconstr
->initdeferred
= false;
9412 nnconstr
->location
= -1;
9413 nnconstr
->keys
= list_make1(makeString(elem
->name
));
9414 nnconstr
->skip_validation
= false;
9415 nnconstr
->initially_valid
= true;
9417 newconstrs
= lappend(newconstrs
, nnconstr
);
9420 /* Finally, add AT subcommands to add each constraint to each child. */
9421 children
= find_inheritance_children(RelationGetRelid(rel
), NoLock
);
9422 foreach_oid(childrelid
, children
)
9424 Relation childrel
= table_open(childrelid
, NoLock
);
9425 AlterTableCmd
*newcmd
= makeNode(AlterTableCmd
);
9428 newcmd
->subtype
= AT_AddConstraint
;
9429 newcmd
->recurse
= true;
9431 foreach(lc2
, newconstrs
)
9433 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9434 newcmd
->def
= lfirst(lc2
);
9436 ATPrepCmd(wqueue
, childrel
, newcmd
,
9437 true, false, lockmode
, context
);
9440 table_close(childrel
, NoLock
);
9445 * ALTER TABLE ADD INDEX
9447 * There is no such command in the grammar, but parse_utilcmd.c converts
9448 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9449 * us schedule creation of the index at the appropriate time during ALTER.
9451 * Return value is the address of the new index.
9453 static ObjectAddress
9454 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
9455 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9460 ObjectAddress address
;
9462 Assert(IsA(stmt
, IndexStmt
));
9463 Assert(!stmt
->concurrent
);
9465 /* The IndexStmt has already been through transformIndexStmt */
9466 Assert(stmt
->transformed
);
9468 /* suppress schema rights check when rebuilding existing index */
9469 check_rights
= !is_rebuild
;
9470 /* skip index build if phase 3 will do it or we're reusing an old one */
9471 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
9472 /* suppress notices when rebuilding existing index */
9475 address
= DefineIndex(RelationGetRelid(rel
),
9477 InvalidOid
, /* no predefined OID */
9478 InvalidOid
, /* no parent index */
9479 InvalidOid
, /* no parent constraint */
9480 -1, /* total_parts unknown */
9481 true, /* is_alter_table */
9483 false, /* check_not_in_use - we did it already */
9488 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9489 * new index instead of building from scratch. Restore associated fields.
9490 * This may store InvalidSubTransactionId in both fields, in which case
9491 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9492 * this after the CCI that made catalog rows visible to any rebuild. The
9493 * DROP of the old edition of this index will have scheduled the storage
9494 * for deletion at commit, so cancel that pending deletion.
9496 if (RelFileNumberIsValid(stmt
->oldNumber
))
9498 Relation irel
= index_open(address
.objectId
, NoLock
);
9500 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
9501 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
9502 RelationPreserveStorage(irel
->rd_locator
, true);
9503 index_close(irel
, NoLock
);
9510 * ALTER TABLE ADD STATISTICS
9512 * This is no such command in the grammar, but we use this internally to add
9513 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9514 * column type change.
9516 static ObjectAddress
9517 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
9518 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9520 ObjectAddress address
;
9522 Assert(IsA(stmt
, CreateStatsStmt
));
9524 /* The CreateStatsStmt has already been through transformStatsStmt */
9525 Assert(stmt
->transformed
);
9527 address
= CreateStatistics(stmt
);
9533 * ALTER TABLE ADD CONSTRAINT USING INDEX
9535 * Returns the address of the new constraint.
9537 static ObjectAddress
9538 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
9539 IndexStmt
*stmt
, LOCKMODE lockmode
)
9541 Oid index_oid
= stmt
->indexOid
;
9544 IndexInfo
*indexInfo
;
9545 char *constraintName
;
9546 char constraintType
;
9547 ObjectAddress address
;
9550 Assert(IsA(stmt
, IndexStmt
));
9551 Assert(OidIsValid(index_oid
));
9552 Assert(stmt
->isconstraint
);
9555 * Doing this on partitioned tables is not a simple feature to implement,
9556 * so let's punt for now.
9558 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9560 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9561 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9563 indexRel
= index_open(index_oid
, AccessShareLock
);
9565 indexName
= pstrdup(RelationGetRelationName(indexRel
));
9567 indexInfo
= BuildIndexInfo(indexRel
);
9569 /* this should have been checked at parse time */
9570 if (!indexInfo
->ii_Unique
)
9571 elog(ERROR
, "index \"%s\" is not unique", indexName
);
9574 * Determine name to assign to constraint. We require a constraint to
9575 * have the same name as the underlying index; therefore, use the index's
9576 * existing name as the default constraint name, and if the user
9577 * explicitly gives some other name for the constraint, rename the index
9580 constraintName
= stmt
->idxname
;
9581 if (constraintName
== NULL
)
9582 constraintName
= indexName
;
9583 else if (strcmp(constraintName
, indexName
) != 0)
9586 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9587 indexName
, constraintName
)));
9588 RenameRelationInternal(index_oid
, constraintName
, false, true);
9591 /* Extra checks needed if making primary key */
9593 index_check_primary_key(rel
, indexInfo
, true, stmt
);
9595 /* Note we currently don't support EXCLUSION constraints here */
9597 constraintType
= CONSTRAINT_PRIMARY
;
9599 constraintType
= CONSTRAINT_UNIQUE
;
9601 /* Create the catalog entries for the constraint */
9602 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
9603 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
9604 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
9605 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
9606 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
9608 address
= index_constraint_create(rel
,
9615 allowSystemTableMods
,
9616 false); /* is_internal */
9618 index_close(indexRel
, NoLock
);
9624 * ALTER TABLE ADD CONSTRAINT
9626 * Return value is the address of the new constraint; if no constraint was
9627 * added, InvalidObjectAddress is returned.
9629 static ObjectAddress
9630 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9631 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
9634 ObjectAddress address
= InvalidObjectAddress
;
9636 Assert(IsA(newConstraint
, Constraint
));
9639 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9640 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9643 switch (newConstraint
->contype
)
9646 case CONSTR_NOTNULL
:
9648 ATAddCheckNNConstraint(wqueue
, tab
, rel
,
9649 newConstraint
, recurse
, false, is_readd
,
9653 case CONSTR_FOREIGN
:
9656 * Assign or validate constraint name
9658 if (newConstraint
->conname
)
9660 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9661 RelationGetRelid(rel
),
9662 newConstraint
->conname
))
9664 (errcode(ERRCODE_DUPLICATE_OBJECT
),
9665 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9666 newConstraint
->conname
,
9667 RelationGetRelationName(rel
))));
9670 newConstraint
->conname
=
9671 ChooseConstraintName(RelationGetRelationName(rel
),
9672 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
9674 RelationGetNamespace(rel
),
9677 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
9684 elog(ERROR
, "unrecognized constraint type: %d",
9685 (int) newConstraint
->contype
);
9692 * Generate the column-name portion of the constraint name for a new foreign
9693 * key given the list of column names that reference the referenced
9694 * table. This will be passed to ChooseConstraintName along with the parent
9695 * table name and the "fkey" suffix.
9697 * We know that less than NAMEDATALEN characters will actually be used, so we
9698 * can truncate the result once we've generated that many.
9700 * XXX see also ChooseExtendedStatisticNameAddition and
9701 * ChooseIndexNameAddition.
9704 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
9706 char buf
[NAMEDATALEN
* 2];
9711 foreach(lc
, colnames
)
9713 const char *name
= strVal(lfirst(lc
));
9716 buf
[buflen
++] = '_'; /* insert _ between names */
9719 * At this point we have buflen <= NAMEDATALEN. name should be less
9720 * than NAMEDATALEN already, but use strlcpy for paranoia.
9722 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
9723 buflen
+= strlen(buf
+ buflen
);
9724 if (buflen
>= NAMEDATALEN
)
9727 return pstrdup(buf
);
9731 * Add a check or not-null constraint to a single table and its children.
9732 * Returns the address of the constraint added to the parent relation,
9733 * if one gets added, or InvalidObjectAddress otherwise.
9735 * Subroutine for ATExecAddConstraint.
9737 * We must recurse to child tables during execution, rather than using
9738 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9739 * constraints *must* be given the same name, else they won't be seen as
9740 * related later. If the user didn't explicitly specify a name, then
9741 * AddRelationNewConstraints would normally assign different names to the
9742 * child constraints. To fix that, we must capture the name assigned at
9743 * the parent table and pass that down.
9745 static ObjectAddress
9746 ATAddCheckNNConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9747 Constraint
*constr
, bool recurse
, bool recursing
,
9748 bool is_readd
, LOCKMODE lockmode
)
9754 ObjectAddress address
= InvalidObjectAddress
;
9756 /* Guard against stack overflow due to overly deep inheritance tree. */
9757 check_stack_depth();
9759 /* At top level, permission check was done in ATPrepCmd, else do it */
9761 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9764 * Call AddRelationNewConstraints to do the work, making sure it works on
9765 * a copy of the Constraint so transformExpr can't modify the original. It
9766 * returns a list of cooked constraints.
9768 * If the constraint ends up getting merged with a pre-existing one, it's
9769 * omitted from the returned list, which is what we want: we do not need
9770 * to do any validation work. That can only happen at child tables,
9771 * though, since we disallow merging at the top level.
9773 newcons
= AddRelationNewConstraints(rel
, NIL
,
9774 list_make1(copyObject(constr
)),
9775 recursing
|| is_readd
, /* allow_merge */
9776 !recursing
, /* is_local */
9777 is_readd
, /* is_internal */
9778 NULL
); /* queryString not available
9781 /* we don't expect more than one constraint here */
9782 Assert(list_length(newcons
) <= 1);
9784 /* Add each to-be-validated constraint to Phase 3's queue */
9785 foreach(lcon
, newcons
)
9787 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9789 if (!ccon
->skip_validation
&& ccon
->contype
!= CONSTR_NOTNULL
)
9791 NewConstraint
*newcon
;
9793 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9794 newcon
->name
= ccon
->name
;
9795 newcon
->contype
= ccon
->contype
;
9796 newcon
->qual
= ccon
->expr
;
9798 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9801 /* Save the actually assigned name if it was defaulted */
9802 if (constr
->conname
== NULL
)
9803 constr
->conname
= ccon
->name
;
9806 * If adding a not-null constraint, set the pg_attribute flag and tell
9807 * phase 3 to verify existing rows, if needed.
9809 if (constr
->contype
== CONSTR_NOTNULL
)
9810 set_attnotnull(wqueue
, rel
, ccon
->attnum
,
9811 !ccon
->is_no_inherit
, lockmode
);
9813 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9816 /* At this point we must have a locked-down name to use */
9817 Assert(newcons
== NIL
|| constr
->conname
!= NULL
);
9819 /* Advance command counter in case same table is visited multiple times */
9820 CommandCounterIncrement();
9823 * If the constraint got merged with an existing constraint, we're done.
9824 * We mustn't recurse to child tables in this case, because they've
9825 * already got the constraint, and visiting them again would lead to an
9826 * incorrect value for coninhcount.
9832 * If adding a NO INHERIT constraint, no need to find our children.
9834 if (constr
->is_no_inherit
)
9838 * Propagate to children as appropriate. Unlike most other ALTER
9839 * routines, we have to do this one level of recursion at a time; we can't
9840 * use find_all_inheritors to do it in one pass.
9843 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9846 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9847 * constraint creation only if there are no children currently. Error out
9850 if (!recurse
&& children
!= NIL
)
9852 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9853 errmsg("constraint must be added to child tables too")));
9856 * The constraint must appear as inherited in children, so create a
9857 * modified constraint object to use.
9859 constr
= copyObject(constr
);
9860 constr
->inhcount
= 1;
9861 foreach(child
, children
)
9863 Oid childrelid
= lfirst_oid(child
);
9865 AlteredTableInfo
*childtab
;
9867 /* find_inheritance_children already got lock */
9868 childrel
= table_open(childrelid
, NoLock
);
9869 CheckTableNotInUse(childrel
, "ALTER TABLE");
9871 /* Find or create work queue entry for this table */
9872 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9875 * Recurse to child. XXX if we didn't create a constraint on the
9876 * parent because it already existed, and we do create one on a child,
9877 * should we return that child's constraint ObjectAddress here?
9879 ATAddCheckNNConstraint(wqueue
, childtab
, childrel
,
9880 constr
, recurse
, true, is_readd
, lockmode
);
9882 table_close(childrel
, NoLock
);
9889 * Add a foreign-key constraint to a single table; return the new constraint's
9892 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9893 * lock on the rel, and have done appropriate validity checks for it.
9894 * We do permissions checks here, however.
9896 * When the referenced or referencing tables (or both) are partitioned,
9897 * multiple pg_constraint rows are required -- one for each partitioned table
9898 * and each partition on each side (fortunately, not one for every combination
9899 * thereof). We also need action triggers on each leaf partition on the
9900 * referenced side, and check triggers on each leaf partition on the
9903 static ObjectAddress
9904 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9905 Constraint
*fkconstraint
,
9906 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9909 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9910 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9911 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9912 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9913 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9914 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9915 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9916 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9917 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9919 bool pk_has_without_overlaps
;
9926 ObjectAddress address
;
9927 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9930 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9931 * delete rows out from under us.
9933 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9934 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9936 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9939 * Validity checks (permission checks wait till we have the column
9942 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9946 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9947 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9948 RelationGetRelationName(rel
),
9949 RelationGetRelationName(pkrel
))));
9950 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9952 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9953 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9954 RelationGetRelationName(rel
),
9955 RelationGetRelationName(pkrel
)),
9956 errdetail("This feature is not yet supported on partitioned tables.")));
9959 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9960 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9962 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9963 errmsg("referenced relation \"%s\" is not a table",
9964 RelationGetRelationName(pkrel
))));
9966 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9968 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9969 errmsg("permission denied: \"%s\" is a system catalog",
9970 RelationGetRelationName(pkrel
))));
9973 * References from permanent or unlogged tables to temp tables, and from
9974 * permanent tables to unlogged tables, are disallowed because the
9975 * referenced data can vanish out from under us. References from temp
9976 * tables to any other table type are also disallowed, because other
9977 * backends might need to run the RI triggers on the perm table, but they
9978 * can't reliably see tuples in the local buffers of other backends.
9980 switch (rel
->rd_rel
->relpersistence
)
9982 case RELPERSISTENCE_PERMANENT
:
9983 if (!RelationIsPermanent(pkrel
))
9985 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9986 errmsg("constraints on permanent tables may reference only permanent tables")));
9988 case RELPERSISTENCE_UNLOGGED
:
9989 if (!RelationIsPermanent(pkrel
)
9990 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9992 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9993 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9995 case RELPERSISTENCE_TEMP
:
9996 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9998 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9999 errmsg("constraints on temporary tables may reference only temporary tables")));
10000 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
10002 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
10003 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10008 * Look up the referencing attributes to make sure they exist, and record
10009 * their attnums and type OIDs.
10011 numfks
= transformColumnNameList(RelationGetRelid(rel
),
10012 fkconstraint
->fk_attrs
,
10013 fkattnum
, fktypoid
);
10014 with_period
= fkconstraint
->fk_with_period
|| fkconstraint
->pk_with_period
;
10015 if (with_period
&& !fkconstraint
->fk_with_period
)
10017 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10018 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10020 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
10021 fkconstraint
->fk_del_set_cols
,
10022 fkdelsetcols
, NULL
);
10023 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
10024 numfkdelsetcols
, fkdelsetcols
,
10025 fkconstraint
->fk_del_set_cols
);
10028 * If the attribute list for the referenced table was omitted, lookup the
10029 * definition of the primary key and use it. Otherwise, validate the
10030 * supplied attribute list. In either case, discover the index OID and
10031 * index opclasses, and the attnums and type OIDs of the attributes.
10033 if (fkconstraint
->pk_attrs
== NIL
)
10035 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
10036 &fkconstraint
->pk_attrs
,
10037 pkattnum
, pktypoid
,
10038 opclasses
, &pk_has_without_overlaps
);
10040 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10041 if (pk_has_without_overlaps
&& !fkconstraint
->fk_with_period
)
10043 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10044 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10048 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
10049 fkconstraint
->pk_attrs
,
10050 pkattnum
, pktypoid
);
10052 /* Since we got pk_attrs, one should be a period. */
10053 if (with_period
&& !fkconstraint
->pk_with_period
)
10055 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10056 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10058 /* Look for an index matching the column list */
10059 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
10060 with_period
, opclasses
, &pk_has_without_overlaps
);
10064 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10067 if (pk_has_without_overlaps
&& !with_period
)
10069 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10070 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10073 * Now we can check permissions.
10075 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
10078 * Check some things for generated columns.
10080 for (i
= 0; i
< numfks
; i
++)
10082 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
10087 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10089 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
10090 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
10091 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
10093 (errcode(ERRCODE_SYNTAX_ERROR
),
10094 errmsg("invalid %s action for foreign key constraint containing generated column",
10096 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
10097 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
10099 (errcode(ERRCODE_SYNTAX_ERROR
),
10100 errmsg("invalid %s action for foreign key constraint containing generated column",
10106 * Some actions are currently unsupported for foreign keys using PERIOD.
10108 if (fkconstraint
->fk_with_period
)
10110 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
||
10111 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
10112 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
)
10114 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
10115 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10118 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_CASCADE
||
10119 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
10120 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
10122 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
10123 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10128 * Look up the equality operators to use in the constraint.
10130 * Note that we have to be careful about the difference between the actual
10131 * PK column type and the opclass' declared input type, which might be
10132 * only binary-compatible with it. The declared opcintype is the right
10133 * thing to probe pg_amop with.
10135 if (numfks
!= numpks
)
10137 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10138 errmsg("number of referencing and referenced columns for foreign key disagree")));
10141 * On the strength of a previous constraint, we might avoid scanning
10142 * tables to validate this one. See below.
10144 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
10145 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
10147 for (i
= 0; i
< numpks
; i
++)
10149 Oid pktype
= pktypoid
[i
];
10150 Oid fktype
= fktypoid
[i
];
10153 Form_pg_opclass cla_tup
;
10163 /* We need several fields out of the pg_opclass entry */
10164 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
10165 if (!HeapTupleIsValid(cla_ht
))
10166 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
10167 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
10168 amid
= cla_tup
->opcmethod
;
10169 opfamily
= cla_tup
->opcfamily
;
10170 opcintype
= cla_tup
->opcintype
;
10171 ReleaseSysCache(cla_ht
);
10175 StrategyNumber rtstrategy
;
10176 bool for_overlaps
= with_period
&& i
== numpks
- 1;
10179 * GiST indexes are required to support temporal foreign keys
10180 * because they combine equals and overlaps.
10182 if (amid
!= GIST_AM_OID
)
10183 elog(ERROR
, "only GiST indexes are supported for temporal foreign keys");
10185 rtstrategy
= for_overlaps
? RTOverlapStrategyNumber
: RTEqualStrategyNumber
;
10188 * An opclass can use whatever strategy numbers it wants, so we
10189 * ask the opclass what number it actually uses instead of our RT*
10192 eqstrategy
= GistTranslateStratnum(opclasses
[i
], rtstrategy
);
10193 if (eqstrategy
== InvalidStrategy
)
10197 tuple
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
10198 if (!HeapTupleIsValid(tuple
))
10199 elog(ERROR
, "cache lookup failed for operator class %u", opclasses
[i
]);
10202 errcode(ERRCODE_UNDEFINED_OBJECT
),
10204 ? errmsg("could not identify an overlaps operator for foreign key")
10205 : errmsg("could not identify an equality operator for foreign key"),
10206 errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10207 rtstrategy
, NameStr(((Form_pg_opclass
) GETSTRUCT(tuple
))->opcname
), "gist"));
10213 * Check it's a btree; currently this can never fail since no
10214 * other index AMs support unique indexes. If we ever did have
10215 * other types of unique indexes, we'd need a way to determine
10216 * which operator strategy number is equality. (We could use
10217 * something like GistTranslateStratnum.)
10219 if (amid
!= BTREE_AM_OID
)
10220 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
10221 eqstrategy
= BTEqualStrategyNumber
;
10225 * There had better be a primary equality operator for the index.
10226 * We'll use it for PK = PK comparisons.
10228 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
10231 if (!OidIsValid(ppeqop
))
10232 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
10233 eqstrategy
, opcintype
, opcintype
, opfamily
);
10236 * Are there equality operators that take exactly the FK type? Assume
10237 * we should look through any domain here.
10239 fktyped
= getBaseType(fktype
);
10241 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
10243 if (OidIsValid(pfeqop
))
10245 pfeqop_right
= fktyped
;
10246 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
10251 /* keep compiler quiet */
10252 pfeqop_right
= InvalidOid
;
10253 ffeqop
= InvalidOid
;
10256 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10259 * Otherwise, look for an implicit cast from the FK type to the
10260 * opcintype, and if found, use the primary equality operator.
10261 * This is a bit tricky because opcintype might be a polymorphic
10262 * type such as ANYARRAY or ANYENUM; so what we have to test is
10263 * whether the two actual column types can be concurrently cast to
10264 * that type. (Otherwise, we'd fail to reject combinations such
10265 * as int[] and point[].)
10267 Oid input_typeids
[2];
10268 Oid target_typeids
[2];
10270 input_typeids
[0] = pktype
;
10271 input_typeids
[1] = fktype
;
10272 target_typeids
[0] = opcintype
;
10273 target_typeids
[1] = opcintype
;
10274 if (can_coerce_type(2, input_typeids
, target_typeids
,
10275 COERCION_IMPLICIT
))
10277 pfeqop
= ffeqop
= ppeqop
;
10278 pfeqop_right
= opcintype
;
10282 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10284 (errcode(ERRCODE_DATATYPE_MISMATCH
),
10285 errmsg("foreign key constraint \"%s\" cannot be implemented",
10286 fkconstraint
->conname
),
10287 errdetail("Key columns \"%s\" and \"%s\" "
10288 "are of incompatible types: %s and %s.",
10289 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
10290 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
10291 format_type_be(fktype
),
10292 format_type_be(pktype
))));
10297 * When a pfeqop changes, revalidate the constraint. We could
10298 * permit intra-opfamily changes, but that adds subtle complexity
10299 * without any concrete benefit for core types. We need not
10300 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10302 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
10303 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
10310 CoercionPathType old_pathtype
;
10311 CoercionPathType new_pathtype
;
10314 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
10318 * Identify coercion pathways from each of the old and new FK-side
10319 * column types to the right (foreign) operand type of the pfeqop.
10320 * We may assume that pg_constraint.conkey is not changing.
10322 old_fktype
= attr
->atttypid
;
10323 new_fktype
= fktype
;
10324 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
10326 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
10330 * Upon a change to the cast from the FK column to its pfeqop
10331 * operand, revalidate the constraint. For this evaluation, a
10332 * binary coercion cast is equivalent to no cast at all. While
10333 * type implementors should design implicit casts with an eye
10334 * toward consistency of operations like equality, we cannot
10335 * assume here that they have done so.
10337 * A function with a polymorphic argument could change behavior
10338 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10339 * when the cast destination is polymorphic, we only avoid
10340 * revalidation if the input type has not changed at all. Given
10341 * just the core data types and operator classes, this requirement
10342 * prevents no would-be optimizations.
10344 * If the cast converts from a base type to a domain thereon, then
10345 * that domain type must be the opcintype of the unique index.
10346 * Necessarily, the primary key column must then be of the domain
10347 * type. Since the constraint was previously valid, all values on
10348 * the foreign side necessarily exist on the primary side and in
10349 * turn conform to the domain. Consequently, we need not treat
10350 * domains specially here.
10352 * Since we require that all collations share the same notion of
10353 * equality (which they do, because texteq reduces to bitwise
10354 * equality), we don't compare collation here.
10356 * We need not directly consider the PK type. It's necessarily
10357 * binary coercible to the opcintype of the unique index column,
10358 * and ri_triggers.c will only deal with PK datums in terms of
10359 * that opcintype. Changing the opcintype also changes pfeqop.
10361 old_check_ok
= (new_pathtype
== old_pathtype
&&
10362 new_castfunc
== old_castfunc
&&
10363 (!IsPolymorphicType(pfeqop_right
) ||
10364 new_fktype
== old_fktype
));
10367 pfeqoperators
[i
] = pfeqop
;
10368 ppeqoperators
[i
] = ppeqop
;
10369 ffeqoperators
[i
] = ffeqop
;
10373 * For FKs with PERIOD we need additional operators to check whether the
10374 * referencing row's range is contained by the aggregated ranges of the
10375 * referenced row(s). For rangetypes and multirangetypes this is
10376 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10377 * support for now. FKs will look these up at "runtime", but we should
10378 * make sure the lookup works here, even if we don't use the values.
10383 Oid aggedperiodoperoid
;
10385 FindFKPeriodOpers(opclasses
[numpks
- 1], &periodoperoid
, &aggedperiodoperoid
);
10389 * Create all the constraint and trigger objects, recursing to partitions
10390 * as necessary. First handle the referenced side.
10392 address
= addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, pkrel
,
10394 InvalidOid
, /* no parent constraint */
10404 InvalidOid
, InvalidOid
,
10407 /* Now handle the referencing side. */
10408 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
10421 InvalidOid
, InvalidOid
,
10425 * Done. Close pk table, but keep lock until we've committed.
10427 table_close(pkrel
, NoLock
);
10433 * validateFkOnDeleteSetColumns
10434 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10435 * column lists are valid.
10438 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
10439 int numfksetcols
, const int16
*fksetcolsattnums
,
10442 for (int i
= 0; i
< numfksetcols
; i
++)
10444 int16 setcol_attnum
= fksetcolsattnums
[i
];
10447 for (int j
= 0; j
< numfks
; j
++)
10449 if (fkattnums
[j
] == setcol_attnum
)
10458 char *col
= strVal(list_nth(fksetcols
, i
));
10461 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
10462 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
10468 * addFkRecurseReferenced
10469 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10470 * side of the constraint
10472 * Create pg_constraint rows for the referenced side of the constraint,
10473 * referencing the parent of the referencing side; also create action triggers
10474 * on leaf partitions. If the table is partitioned, recurse to handle each
10477 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10478 * of an ALTER TABLE sequence.
10479 * fkconstraint is the constraint being added.
10480 * rel is the root referencing relation.
10481 * pkrel is the referenced relation; might be a partition, if recursing.
10482 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10483 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10484 * top-level constraint.
10485 * numfks is the number of columns in the foreign key
10486 * pkattnum is the attnum array of referenced attributes.
10487 * fkattnum is the attnum array of referencing attributes.
10488 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10490 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10491 * NULL/DEFAULT clause
10492 * pf/pp/ffeqoperators are OID array of operators between columns.
10493 * old_check_ok signals that this constraint replaces an existing one that
10494 * was already validated (thus this one doesn't need validation).
10495 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10496 * a partition, are the OIDs of the parent action triggers for DELETE and
10497 * UPDATE respectively.
10499 static ObjectAddress
10500 addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10501 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10503 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
10504 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10505 int numfkdelsetcols
, int16
*fkdelsetcols
,
10507 Oid parentDelTrigger
, Oid parentUpdTrigger
,
10510 ObjectAddress address
;
10516 Oid deleteTriggerOid
,
10520 * Verify relkind for each referenced partition. At the top level, this
10521 * is redundant with a previous check, but we need it when recursing.
10523 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
10524 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
10526 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10527 errmsg("referenced relation \"%s\" is not a table",
10528 RelationGetRelationName(pkrel
))));
10531 * Caller supplies us with a constraint name; however, it may be used in
10532 * this partition, so come up with a different one in that case.
10534 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10535 RelationGetRelid(rel
),
10536 fkconstraint
->conname
))
10537 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
10538 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10540 RelationGetNamespace(rel
), NIL
);
10542 conname
= fkconstraint
->conname
;
10544 if (OidIsValid(parentConstr
))
10546 conislocal
= false;
10548 connoinherit
= false;
10556 * always inherit for partitioned tables, never for legacy inheritance
10558 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
10562 * Record the FK constraint in pg_constraint.
10564 constrOid
= CreateConstraintEntry(conname
,
10565 RelationGetNamespace(rel
),
10566 CONSTRAINT_FOREIGN
,
10567 fkconstraint
->deferrable
,
10568 fkconstraint
->initdeferred
,
10569 fkconstraint
->initially_valid
,
10571 RelationGetRelid(rel
),
10575 InvalidOid
, /* not a domain constraint */
10577 RelationGetRelid(pkrel
),
10583 fkconstraint
->fk_upd_action
,
10584 fkconstraint
->fk_del_action
,
10587 fkconstraint
->fk_matchtype
,
10588 NULL
, /* no exclusion constraint */
10589 NULL
, /* no check constraint */
10591 conislocal
, /* islocal */
10592 coninhcount
, /* inhcount */
10593 connoinherit
, /* conNoInherit */
10594 with_period
, /* conPeriod */
10595 false); /* is_internal */
10597 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10600 * Mark the child constraint as part of the parent constraint; it must not
10601 * be dropped on its own. (This constraint is deleted when the partition
10602 * is detached, but a special check needs to occur that the partition
10603 * contains no referenced values.)
10605 if (OidIsValid(parentConstr
))
10607 ObjectAddress referenced
;
10609 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10610 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
10613 /* make new constraint visible, in case we add more */
10614 CommandCounterIncrement();
10617 * Create the action triggers that enforce the constraint.
10619 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
10621 constrOid
, indexOid
,
10622 parentDelTrigger
, parentUpdTrigger
,
10623 &deleteTriggerOid
, &updateTriggerOid
);
10626 * If the referenced table is partitioned, recurse on ourselves to handle
10627 * each partition. We need one pg_constraint row created for each
10628 * partition in addition to the pg_constraint row for the parent table.
10630 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10632 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
10634 for (int i
= 0; i
< pd
->nparts
; i
++)
10638 AttrNumber
*mapped_pkattnum
;
10641 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
10644 * Map the attribute numbers in the referenced side of the FK
10645 * definition to match the partition's column layout.
10647 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
10648 RelationGetDescr(pkrel
),
10652 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
10653 for (int j
= 0; j
< numfks
; j
++)
10654 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
10657 mapped_pkattnum
= pkattnum
;
10660 partIndexId
= index_get_partition(partRel
, indexOid
);
10661 if (!OidIsValid(partIndexId
))
10662 elog(ERROR
, "index for %u not found in partition %s",
10663 indexOid
, RelationGetRelationName(partRel
));
10664 addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, partRel
,
10665 partIndexId
, constrOid
, numfks
,
10666 mapped_pkattnum
, fkattnum
,
10667 pfeqoperators
, ppeqoperators
, ffeqoperators
,
10668 numfkdelsetcols
, fkdelsetcols
,
10670 deleteTriggerOid
, updateTriggerOid
,
10673 /* Done -- clean up (but keep the lock) */
10674 table_close(partRel
, NoLock
);
10677 pfree(mapped_pkattnum
);
10687 * addFkRecurseReferencing
10688 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10690 * If the referencing relation is a plain relation, create the necessary check
10691 * triggers that implement the constraint, and set up for Phase 3 constraint
10692 * verification. If the referencing relation is a partitioned table, then
10693 * we create a pg_constraint row for it and recurse on this routine for each
10696 * We assume that the referenced relation is locked against concurrent
10697 * deletions. If it's a partitioned relation, every partition must be so
10700 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10701 * of an ALTER TABLE sequence.
10702 * fkconstraint is the constraint being added.
10703 * rel is the referencing relation; might be a partition, if recursing.
10704 * pkrel is the root referenced relation.
10705 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10706 * parentConstr is the OID of the parent constraint (there is always one).
10707 * numfks is the number of columns in the foreign key
10708 * pkattnum is the attnum array of referenced attributes.
10709 * fkattnum is the attnum array of referencing attributes.
10710 * pf/pp/ffeqoperators are OID array of operators between columns.
10711 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10713 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10714 * NULL/DEFAULT clause
10715 * old_check_ok signals that this constraint replaces an existing one that
10716 * was already validated (thus this one doesn't need validation).
10717 * lockmode is the lockmode to acquire on partitions when recursing.
10718 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10719 * a partition, are the OIDs of the parent check triggers for INSERT and
10720 * UPDATE respectively.
10723 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10724 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10725 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
10726 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10727 int numfkdelsetcols
, int16
*fkdelsetcols
,
10728 bool old_check_ok
, LOCKMODE lockmode
,
10729 Oid parentInsTrigger
, Oid parentUpdTrigger
,
10732 Oid insertTriggerOid
,
10735 Assert(OidIsValid(parentConstr
));
10737 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10739 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10740 errmsg("foreign key constraints are not supported on foreign tables")));
10743 * Add the check triggers to it and, if necessary, schedule it to be
10744 * checked in Phase 3.
10746 * If the relation is partitioned, drill down to do it to its partitions.
10748 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
10749 RelationGetRelid(pkrel
),
10753 parentInsTrigger
, parentUpdTrigger
,
10754 &insertTriggerOid
, &updateTriggerOid
);
10756 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
10759 * Tell Phase 3 to check that the constraint is satisfied by existing
10760 * rows. We can skip this during table creation, when requested
10761 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10762 * and when we're recreating a constraint following a SET DATA TYPE
10763 * operation that did not impugn its validity.
10765 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
10767 NewConstraint
*newcon
;
10768 AlteredTableInfo
*tab
;
10770 tab
= ATGetQueueEntry(wqueue
, rel
);
10772 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
10773 newcon
->name
= get_constraint_name(parentConstr
);
10774 newcon
->contype
= CONSTR_FOREIGN
;
10775 newcon
->refrelid
= RelationGetRelid(pkrel
);
10776 newcon
->refindid
= indexOid
;
10777 newcon
->conid
= parentConstr
;
10778 newcon
->conwithperiod
= fkconstraint
->fk_with_period
;
10779 newcon
->qual
= (Node
*) fkconstraint
;
10781 tab
->constraints
= lappend(tab
->constraints
, newcon
);
10784 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10786 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
10790 * Triggers of the foreign keys will be manipulated a bunch of times
10791 * in the loop below. To avoid repeatedly opening/closing the trigger
10792 * catalog relation, we open it here and pass it to the subroutines
10795 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10798 * Recurse to take appropriate action on each partition; either we
10799 * find an existing constraint to reparent to ours, or we create a new
10802 for (int i
= 0; i
< pd
->nparts
; i
++)
10804 Oid partitionId
= pd
->oids
[i
];
10805 Relation partition
= table_open(partitionId
, lockmode
);
10808 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
10812 ObjectAddress address
,
10816 CheckTableNotInUse(partition
, "ALTER TABLE");
10818 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
10819 RelationGetDescr(rel
),
10821 for (int j
= 0; j
< numfks
; j
++)
10822 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
10824 /* Check whether an existing constraint can be repurposed */
10825 partFKs
= copyObject(RelationGetFKeyList(partition
));
10827 foreach(cell
, partFKs
)
10829 ForeignKeyCacheInfo
*fk
;
10831 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
10832 if (tryAttachPartitionForeignKey(fk
,
10849 table_close(partition
, NoLock
);
10854 * No luck finding a good constraint to reuse; create our own.
10856 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10857 RelationGetRelid(partition
),
10858 fkconstraint
->conname
))
10859 conname
= ChooseConstraintName(RelationGetRelationName(partition
),
10860 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10862 RelationGetNamespace(partition
), NIL
);
10864 conname
= fkconstraint
->conname
;
10866 CreateConstraintEntry(conname
,
10867 RelationGetNamespace(partition
),
10868 CONSTRAINT_FOREIGN
,
10869 fkconstraint
->deferrable
,
10870 fkconstraint
->initdeferred
,
10871 fkconstraint
->initially_valid
,
10879 RelationGetRelid(pkrel
),
10885 fkconstraint
->fk_upd_action
,
10886 fkconstraint
->fk_del_action
,
10889 fkconstraint
->fk_matchtype
,
10896 with_period
, /* conPeriod */
10900 * Give this constraint partition-type dependencies on the parent
10901 * constraint as well as the table.
10903 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10904 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10905 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10906 ObjectAddressSet(referenced
, RelationRelationId
, partitionId
);
10907 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10909 /* Make all this visible before recursing */
10910 CommandCounterIncrement();
10912 /* call ourselves to finalize the creation and we're done */
10913 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10930 table_close(partition
, NoLock
);
10933 table_close(trigrel
, RowExclusiveLock
);
10938 * CloneForeignKeyConstraints
10939 * Clone foreign keys from a partitioned table to a newly acquired
10942 * partitionRel is a partition of parentRel, so we can be certain that it has
10943 * the same columns with the same datatypes. The columns may be in different
10946 * wqueue must be passed to set up phase 3 constraint checking, unless the
10947 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10951 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10952 Relation partitionRel
)
10954 /* This only works for declarative partitioning */
10955 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10958 * Clone constraints for which the parent is on the referenced side.
10960 CloneFkReferenced(parentRel
, partitionRel
);
10963 * Now clone constraints where the parent is on the referencing side.
10965 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10969 * CloneFkReferenced
10970 * Subroutine for CloneForeignKeyConstraints
10972 * Find all the FKs that have the parent relation on the referenced side;
10973 * clone those constraints to the given partition. This is to be called
10974 * when the partition is being created or attached.
10976 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10978 * This recurses to partitions, if the relation being attached is partitioned.
10979 * Recursion is done by calling addFkRecurseReferenced.
10982 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10984 Relation pg_constraint
;
10988 ScanKeyData key
[2];
10994 * Search for any constraints where this partition's parent is in the
10995 * referenced side. However, we must not clone any constraint whose
10996 * parent constraint is also going to be cloned, to avoid duplicates. So
10997 * do it in two steps: first construct the list of constraints to clone,
10998 * then go over that list cloning those whose parents are not in the list.
10999 * (We must not rely on the parent being seen first, since the catalog
11000 * scan could return children first.)
11002 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
11003 ScanKeyInit(&key
[0],
11004 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
11005 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
11006 ScanKeyInit(&key
[1],
11007 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
11008 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
11009 /* This is a seqscan, as we don't have a usable index ... */
11010 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
11012 while ((tuple
= systable_getnext(scan
)) != NULL
)
11014 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11016 clone
= lappend_oid(clone
, constrForm
->oid
);
11018 systable_endscan(scan
);
11019 table_close(pg_constraint
, RowShareLock
);
11022 * Triggers of the foreign keys will be manipulated a bunch of times in
11023 * the loop below. To avoid repeatedly opening/closing the trigger
11024 * catalog relation, we open it here and pass it to the subroutines called
11027 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11029 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
11030 RelationGetDescr(parentRel
),
11032 foreach(cell
, clone
)
11034 Oid constrOid
= lfirst_oid(cell
);
11035 Form_pg_constraint constrForm
;
11040 AttrNumber conkey
[INDEX_MAX_KEYS
];
11041 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
11042 AttrNumber confkey
[INDEX_MAX_KEYS
];
11043 Oid conpfeqop
[INDEX_MAX_KEYS
];
11044 Oid conppeqop
[INDEX_MAX_KEYS
];
11045 Oid conffeqop
[INDEX_MAX_KEYS
];
11046 int numfkdelsetcols
;
11047 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
11048 Constraint
*fkconstraint
;
11049 Oid deleteTriggerOid
,
11052 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
11053 if (!HeapTupleIsValid(tuple
))
11054 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
11055 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11058 * As explained above: don't try to clone a constraint for which we're
11059 * going to clone the parent.
11061 if (list_member_oid(clone
, constrForm
->conparentid
))
11063 ReleaseSysCache(tuple
);
11068 * Don't clone self-referencing foreign keys, which can be in the
11069 * partitioned table or in the partition-to-be.
11071 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
11072 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
11074 ReleaseSysCache(tuple
);
11079 * Because we're only expanding the key space at the referenced side,
11080 * we don't need to prevent any operation in the referencing table, so
11081 * AccessShareLock suffices (assumes that dropping the constraint
11084 fkRel
= table_open(constrForm
->conrelid
, AccessShareLock
);
11086 indexOid
= constrForm
->conindid
;
11087 DeconstructFkConstraintRow(tuple
,
11097 for (int i
= 0; i
< numfks
; i
++)
11098 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
11100 fkconstraint
= makeNode(Constraint
);
11101 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11102 fkconstraint
->conname
= NameStr(constrForm
->conname
);
11103 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11104 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11105 fkconstraint
->location
= -1;
11106 fkconstraint
->pktable
= NULL
;
11107 /* ->fk_attrs determined below */
11108 fkconstraint
->pk_attrs
= NIL
;
11109 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11110 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11111 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11112 fkconstraint
->fk_del_set_cols
= NIL
;
11113 fkconstraint
->old_conpfeqop
= NIL
;
11114 fkconstraint
->old_pktable_oid
= InvalidOid
;
11115 fkconstraint
->skip_validation
= false;
11116 fkconstraint
->initially_valid
= true;
11118 /* set up colnames that are used to generate the constraint name */
11119 for (int i
= 0; i
< numfks
; i
++)
11121 Form_pg_attribute att
;
11123 att
= TupleDescAttr(RelationGetDescr(fkRel
),
11125 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11126 makeString(NameStr(att
->attname
)));
11130 * Add the new foreign key constraint pointing to the new partition.
11131 * Because this new partition appears in the referenced side of the
11132 * constraint, we don't need to set up for Phase 3 check.
11134 partIndexId
= index_get_partition(partitionRel
, indexOid
);
11135 if (!OidIsValid(partIndexId
))
11136 elog(ERROR
, "index for %u not found in partition %s",
11137 indexOid
, RelationGetRelationName(partitionRel
));
11140 * Get the "action" triggers belonging to the constraint to pass as
11141 * parent OIDs for similar triggers that will be created on the
11142 * partition in addFkRecurseReferenced().
11144 GetForeignKeyActionTriggers(trigrel
, constrOid
,
11145 constrForm
->confrelid
, constrForm
->conrelid
,
11146 &deleteTriggerOid
, &updateTriggerOid
);
11148 addFkRecurseReferenced(NULL
,
11165 constrForm
->conperiod
);
11167 table_close(fkRel
, NoLock
);
11168 ReleaseSysCache(tuple
);
11171 table_close(trigrel
, RowExclusiveLock
);
11175 * CloneFkReferencing
11176 * Subroutine for CloneForeignKeyConstraints
11178 * For each FK constraint of the parent relation in the given list, find an
11179 * equivalent constraint in its partition relation that can be reparented;
11180 * if one cannot be found, create a new constraint in the partition as its
11183 * If wqueue is given, it is used to set up phase-3 verification for each
11184 * cloned constraint; if omitted, we assume that such verification is not
11185 * needed (example: the partition is being created anew).
11188 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
11196 /* obtain a list of constraints that we need to clone */
11197 foreach(cell
, RelationGetFKeyList(parentRel
))
11199 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
11201 clone
= lappend_oid(clone
, fk
->conoid
);
11205 * Silently do nothing if there's nothing to do. In particular, this
11206 * avoids throwing a spurious error for foreign tables.
11211 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
11213 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11214 errmsg("foreign key constraints are not supported on foreign tables")));
11217 * Triggers of the foreign keys will be manipulated a bunch of times in
11218 * the loop below. To avoid repeatedly opening/closing the trigger
11219 * catalog relation, we open it here and pass it to the subroutines called
11222 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11225 * The constraint key may differ, if the columns in the partition are
11226 * different. This map is used to convert them.
11228 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
11229 RelationGetDescr(parentRel
),
11232 partFKs
= copyObject(RelationGetFKeyList(partRel
));
11234 foreach(cell
, clone
)
11236 Oid parentConstrOid
= lfirst_oid(cell
);
11237 Form_pg_constraint constrForm
;
11241 AttrNumber conkey
[INDEX_MAX_KEYS
];
11242 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
11243 AttrNumber confkey
[INDEX_MAX_KEYS
];
11244 Oid conpfeqop
[INDEX_MAX_KEYS
];
11245 Oid conppeqop
[INDEX_MAX_KEYS
];
11246 Oid conffeqop
[INDEX_MAX_KEYS
];
11247 int numfkdelsetcols
;
11248 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
11249 Constraint
*fkconstraint
;
11253 ObjectAddress address
,
11256 Oid insertTriggerOid
,
11260 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
11261 if (!HeapTupleIsValid(tuple
))
11262 elog(ERROR
, "cache lookup failed for constraint %u",
11264 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11266 /* Don't clone constraints whose parents are being cloned */
11267 if (list_member_oid(clone
, constrForm
->conparentid
))
11269 ReleaseSysCache(tuple
);
11274 * Need to prevent concurrent deletions. If pkrel is a partitioned
11275 * relation, that means to lock all partitions.
11277 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
11278 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11279 (void) find_all_inheritors(RelationGetRelid(pkrel
),
11280 ShareRowExclusiveLock
, NULL
);
11282 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
11283 conpfeqop
, conppeqop
, conffeqop
,
11284 &numfkdelsetcols
, confdelsetcols
);
11285 for (int i
= 0; i
< numfks
; i
++)
11286 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
11289 * Get the "check" triggers belonging to the constraint to pass as
11290 * parent OIDs for similar triggers that will be created on the
11291 * partition in addFkRecurseReferencing(). They are also passed to
11292 * tryAttachPartitionForeignKey() below to simply assign as parents to
11293 * the partition's existing "check" triggers, that is, if the
11294 * corresponding constraints is deemed attachable to the parent
11297 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
11298 constrForm
->confrelid
, constrForm
->conrelid
,
11299 &insertTriggerOid
, &updateTriggerOid
);
11302 * Before creating a new constraint, see whether any existing FKs are
11303 * fit for the purpose. If one is, attach the parent constraint to
11304 * it, and don't clone anything. This way we avoid the expensive
11305 * verification step and don't end up with a duplicate FK, and we
11306 * don't need to recurse to partitions for this constraint.
11309 foreach(lc
, partFKs
)
11311 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
11313 if (tryAttachPartitionForeignKey(fk
,
11314 RelationGetRelid(partRel
),
11325 table_close(pkrel
, NoLock
);
11331 ReleaseSysCache(tuple
);
11335 /* No dice. Set up to create our own constraint */
11336 fkconstraint
= makeNode(Constraint
);
11337 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11338 /* ->conname determined below */
11339 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11340 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11341 fkconstraint
->location
= -1;
11342 fkconstraint
->pktable
= NULL
;
11343 /* ->fk_attrs determined below */
11344 fkconstraint
->pk_attrs
= NIL
;
11345 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11346 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11347 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11348 fkconstraint
->fk_del_set_cols
= NIL
;
11349 fkconstraint
->old_conpfeqop
= NIL
;
11350 fkconstraint
->old_pktable_oid
= InvalidOid
;
11351 fkconstraint
->skip_validation
= false;
11352 fkconstraint
->initially_valid
= true;
11353 for (int i
= 0; i
< numfks
; i
++)
11355 Form_pg_attribute att
;
11357 att
= TupleDescAttr(RelationGetDescr(partRel
),
11358 mapped_conkey
[i
] - 1);
11359 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11360 makeString(NameStr(att
->attname
)));
11362 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
11363 RelationGetRelid(partRel
),
11364 NameStr(constrForm
->conname
)))
11365 fkconstraint
->conname
=
11366 ChooseConstraintName(RelationGetRelationName(partRel
),
11367 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
11369 RelationGetNamespace(partRel
), NIL
);
11371 fkconstraint
->conname
= pstrdup(NameStr(constrForm
->conname
));
11373 indexOid
= constrForm
->conindid
;
11374 with_period
= constrForm
->conperiod
;
11376 CreateConstraintEntry(fkconstraint
->conname
,
11377 constrForm
->connamespace
,
11378 CONSTRAINT_FOREIGN
,
11379 fkconstraint
->deferrable
,
11380 fkconstraint
->initdeferred
,
11381 constrForm
->convalidated
,
11383 RelationGetRelid(partRel
),
11387 InvalidOid
, /* not a domain constraint */
11389 constrForm
->confrelid
, /* same foreign rel */
11395 fkconstraint
->fk_upd_action
,
11396 fkconstraint
->fk_del_action
,
11399 fkconstraint
->fk_matchtype
,
11403 false, /* islocal */
11405 false, /* conNoInherit */
11406 with_period
, /* conPeriod */
11409 /* Set up partition dependencies for the new constraint */
11410 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
11411 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstrOid
);
11412 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
11413 ObjectAddressSet(referenced
, RelationRelationId
,
11414 RelationGetRelid(partRel
));
11415 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
11417 /* Done with the cloned constraint's tuple */
11418 ReleaseSysCache(tuple
);
11420 /* Make all this visible before recursing */
11421 CommandCounterIncrement();
11423 addFkRecurseReferencing(wqueue
,
11437 false, /* no old check exists */
11438 AccessExclusiveLock
,
11442 table_close(pkrel
, NoLock
);
11445 table_close(trigrel
, RowExclusiveLock
);
11449 * When the parent of a partition receives [the referencing side of] a foreign
11450 * key, we must propagate that foreign key to the partition. However, the
11451 * partition might already have an equivalent foreign key; this routine
11452 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11453 * by the other parameters. If they are equivalent, create the link between
11454 * the two constraints and return true.
11456 * If the given FK does not match the one defined by rest of the params,
11460 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
11462 Oid parentConstrOid
,
11464 AttrNumber
*mapped_conkey
,
11465 AttrNumber
*confkey
,
11467 Oid parentInsTrigger
,
11468 Oid parentUpdTrigger
,
11471 HeapTuple parentConstrTup
;
11472 Form_pg_constraint parentConstr
;
11473 HeapTuple partcontup
;
11474 Form_pg_constraint partConstr
;
11478 Oid insertTriggerOid
,
11481 parentConstrTup
= SearchSysCache1(CONSTROID
,
11482 ObjectIdGetDatum(parentConstrOid
));
11483 if (!HeapTupleIsValid(parentConstrTup
))
11484 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
11485 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
11488 * Do some quick & easy initial checks. If any of these fail, we cannot
11489 * use this constraint.
11491 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
11493 ReleaseSysCache(parentConstrTup
);
11496 for (int i
= 0; i
< numfks
; i
++)
11498 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
11499 fk
->confkey
[i
] != confkey
[i
] ||
11500 fk
->conpfeqop
[i
] != conpfeqop
[i
])
11502 ReleaseSysCache(parentConstrTup
);
11508 * Looks good so far; do some more extensive checks. Presumably the check
11509 * for 'convalidated' could be dropped, since we don't really care about
11510 * that, but let's be careful for now.
11512 partcontup
= SearchSysCache1(CONSTROID
,
11513 ObjectIdGetDatum(fk
->conoid
));
11514 if (!HeapTupleIsValid(partcontup
))
11515 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
11516 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
11517 if (OidIsValid(partConstr
->conparentid
) ||
11518 !partConstr
->convalidated
||
11519 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
11520 partConstr
->condeferred
!= parentConstr
->condeferred
||
11521 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
11522 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
11523 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
11525 ReleaseSysCache(parentConstrTup
);
11526 ReleaseSysCache(partcontup
);
11530 ReleaseSysCache(partcontup
);
11531 ReleaseSysCache(parentConstrTup
);
11534 * Looks good! Attach this constraint. The action triggers in the new
11535 * partition become redundant -- the parent table already has equivalent
11536 * ones, and those will be able to reach the partition. Remove the ones
11537 * in the partition. We identify them because they have our constraint
11538 * OID, as well as being on the referenced rel.
11541 Anum_pg_trigger_tgconstraint
,
11542 BTEqualStrategyNumber
, F_OIDEQ
,
11543 ObjectIdGetDatum(fk
->conoid
));
11544 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11546 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11548 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11549 ObjectAddress trigger
;
11551 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
11553 if (trgform
->tgrelid
!= fk
->confrelid
)
11557 * The constraint is originally set up to contain this trigger as an
11558 * implementation object, so there's a dependency record that links
11559 * the two; however, since the trigger is no longer needed, we remove
11560 * the dependency link in order to be able to drop the trigger while
11561 * keeping the constraint intact.
11563 deleteDependencyRecordsFor(TriggerRelationId
,
11566 /* make dependency deletion visible to performDeletion */
11567 CommandCounterIncrement();
11568 ObjectAddressSet(trigger
, TriggerRelationId
,
11570 performDeletion(&trigger
, DROP_RESTRICT
, 0);
11571 /* make trigger drop visible, in case the loop iterates */
11572 CommandCounterIncrement();
11575 systable_endscan(scan
);
11577 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
11580 * Like the constraint, attach partition's "check" triggers to the
11581 * corresponding parent triggers.
11583 GetForeignKeyCheckTriggers(trigrel
,
11584 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
11585 &insertTriggerOid
, &updateTriggerOid
);
11586 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
11587 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
11589 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
11590 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
11593 CommandCounterIncrement();
11598 * GetForeignKeyActionTriggers
11599 * Returns delete and update "action" triggers of the given relation
11600 * belonging to the given constraint
11603 GetForeignKeyActionTriggers(Relation trigrel
,
11604 Oid conoid
, Oid confrelid
, Oid conrelid
,
11605 Oid
*deleteTriggerOid
,
11606 Oid
*updateTriggerOid
)
11612 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
11614 Anum_pg_trigger_tgconstraint
,
11615 BTEqualStrategyNumber
, F_OIDEQ
,
11616 ObjectIdGetDatum(conoid
));
11618 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11620 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11622 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11624 if (trgform
->tgconstrrelid
!= conrelid
)
11626 if (trgform
->tgrelid
!= confrelid
)
11628 /* Only ever look at "action" triggers on the PK side. */
11629 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
11631 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
11633 Assert(*deleteTriggerOid
== InvalidOid
);
11634 *deleteTriggerOid
= trgform
->oid
;
11636 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11638 Assert(*updateTriggerOid
== InvalidOid
);
11639 *updateTriggerOid
= trgform
->oid
;
11641 #ifndef USE_ASSERT_CHECKING
11642 /* In an assert-enabled build, continue looking to find duplicates */
11643 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
11648 if (!OidIsValid(*deleteTriggerOid
))
11649 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
11651 if (!OidIsValid(*updateTriggerOid
))
11652 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
11655 systable_endscan(scan
);
11659 * GetForeignKeyCheckTriggers
11660 * Returns insert and update "check" triggers of the given relation
11661 * belonging to the given constraint
11664 GetForeignKeyCheckTriggers(Relation trigrel
,
11665 Oid conoid
, Oid confrelid
, Oid conrelid
,
11666 Oid
*insertTriggerOid
,
11667 Oid
*updateTriggerOid
)
11673 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
11675 Anum_pg_trigger_tgconstraint
,
11676 BTEqualStrategyNumber
, F_OIDEQ
,
11677 ObjectIdGetDatum(conoid
));
11679 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11681 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11683 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11685 if (trgform
->tgconstrrelid
!= confrelid
)
11687 if (trgform
->tgrelid
!= conrelid
)
11689 /* Only ever look at "check" triggers on the FK side. */
11690 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
11692 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
11694 Assert(*insertTriggerOid
== InvalidOid
);
11695 *insertTriggerOid
= trgform
->oid
;
11697 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11699 Assert(*updateTriggerOid
== InvalidOid
);
11700 *updateTriggerOid
= trgform
->oid
;
11702 #ifndef USE_ASSERT_CHECKING
11703 /* In an assert-enabled build, continue looking to find duplicates. */
11704 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
11709 if (!OidIsValid(*insertTriggerOid
))
11710 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
11712 if (!OidIsValid(*updateTriggerOid
))
11713 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
11716 systable_endscan(scan
);
11720 * ALTER TABLE ALTER CONSTRAINT
11722 * Update the attributes of a constraint.
11724 * Currently only works for Foreign Key constraints.
11726 * If the constraint is modified, returns its address; otherwise, return
11727 * InvalidObjectAddress.
11729 static ObjectAddress
11730 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
11731 bool recursing
, LOCKMODE lockmode
)
11733 Constraint
*cmdcon
;
11737 ScanKeyData skey
[3];
11738 HeapTuple contuple
;
11739 Form_pg_constraint currcon
;
11740 ObjectAddress address
;
11741 List
*otherrelids
= NIL
;
11744 cmdcon
= castNode(Constraint
, cmd
->def
);
11746 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11747 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11750 * Find and check the target constraint
11752 ScanKeyInit(&skey
[0],
11753 Anum_pg_constraint_conrelid
,
11754 BTEqualStrategyNumber
, F_OIDEQ
,
11755 ObjectIdGetDatum(RelationGetRelid(rel
)));
11756 ScanKeyInit(&skey
[1],
11757 Anum_pg_constraint_contypid
,
11758 BTEqualStrategyNumber
, F_OIDEQ
,
11759 ObjectIdGetDatum(InvalidOid
));
11760 ScanKeyInit(&skey
[2],
11761 Anum_pg_constraint_conname
,
11762 BTEqualStrategyNumber
, F_NAMEEQ
,
11763 CStringGetDatum(cmdcon
->conname
));
11764 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11765 true, NULL
, 3, skey
);
11767 /* There can be at most one matching row */
11768 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
11770 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11771 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11772 cmdcon
->conname
, RelationGetRelationName(rel
))));
11774 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11775 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
11777 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11778 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11779 cmdcon
->conname
, RelationGetRelationName(rel
))));
11782 * If it's not the topmost constraint, raise an error.
11784 * Altering a non-topmost constraint leaves some triggers untouched, since
11785 * they are not directly connected to this constraint; also, pg_dump would
11786 * ignore the deferrability status of the individual constraint, since it
11787 * only dumps topmost constraints. Avoid these problems by refusing this
11788 * operation and telling the user to alter the parent constraint instead.
11790 if (OidIsValid(currcon
->conparentid
))
11793 Oid parent
= currcon
->conparentid
;
11794 char *ancestorname
= NULL
;
11795 char *ancestortable
= NULL
;
11797 /* Loop to find the topmost constraint */
11798 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
11800 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
11802 /* If no parent, this is the constraint we want */
11803 if (!OidIsValid(contup
->conparentid
))
11805 ancestorname
= pstrdup(NameStr(contup
->conname
));
11806 ancestortable
= get_rel_name(contup
->conrelid
);
11807 ReleaseSysCache(tp
);
11811 parent
= contup
->conparentid
;
11812 ReleaseSysCache(tp
);
11816 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11817 cmdcon
->conname
, RelationGetRelationName(rel
)),
11818 ancestorname
&& ancestortable
?
11819 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11820 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
11821 errhint("You may alter the constraint it derives from instead.")));
11825 * Do the actual catalog work. We can skip changing if already in the
11826 * desired state, but not if a partitioned table: partitions need to be
11827 * processed regardless, in case they had the constraint locally changed.
11829 address
= InvalidObjectAddress
;
11830 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11831 currcon
->condeferred
!= cmdcon
->initdeferred
||
11832 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11834 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
11835 &otherrelids
, lockmode
))
11836 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
11840 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11841 * having the constraint itself; here we also invalidate for relations
11842 * that have any triggers that are part of the constraint.
11844 foreach(lc
, otherrelids
)
11845 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
11847 systable_endscan(scan
);
11849 table_close(tgrel
, RowExclusiveLock
);
11850 table_close(conrel
, RowExclusiveLock
);
11856 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11857 * constraint is altered.
11859 * *otherrelids is appended OIDs of relations containing affected triggers.
11861 * Note that we must recurse even when the values are correct, in case
11862 * indirect descendants have had their constraints altered locally.
11863 * (This could be avoided if we forbade altering constraints in partitions
11864 * but existing releases don't do that.)
11867 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
11868 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
11871 Form_pg_constraint currcon
;
11874 bool changed
= false;
11876 /* since this function recurses, it could be driven to stack overflow */
11877 check_stack_depth();
11879 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11880 conoid
= currcon
->oid
;
11881 refrelid
= currcon
->confrelid
;
11884 * Update pg_constraint with the flags from cmdcon.
11886 * If called to modify a constraint that's already in the desired state,
11887 * silently do nothing.
11889 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11890 currcon
->condeferred
!= cmdcon
->initdeferred
)
11892 HeapTuple copyTuple
;
11893 Form_pg_constraint copy_con
;
11896 SysScanDesc tgscan
;
11898 copyTuple
= heap_copytuple(contuple
);
11899 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11900 copy_con
->condeferrable
= cmdcon
->deferrable
;
11901 copy_con
->condeferred
= cmdcon
->initdeferred
;
11902 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11904 InvokeObjectPostAlterHook(ConstraintRelationId
,
11907 heap_freetuple(copyTuple
);
11910 /* Make new constraint flags visible to others */
11911 CacheInvalidateRelcache(rel
);
11914 * Now we need to update the multiple entries in pg_trigger that
11915 * implement the constraint.
11917 ScanKeyInit(&tgkey
,
11918 Anum_pg_trigger_tgconstraint
,
11919 BTEqualStrategyNumber
, F_OIDEQ
,
11920 ObjectIdGetDatum(conoid
));
11921 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11923 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11925 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11926 Form_pg_trigger copy_tg
;
11927 HeapTuple tgCopyTuple
;
11930 * Remember OIDs of other relation(s) involved in FK constraint.
11931 * (Note: it's likely that we could skip forcing a relcache inval
11932 * for other rels that don't have a trigger whose properties
11933 * change, but let's be conservative.)
11935 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11936 *otherrelids
= list_append_unique_oid(*otherrelids
,
11940 * Update deferrability of RI_FKey_noaction_del,
11941 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11942 * triggers, but not others; see createForeignKeyActionTriggers
11943 * and CreateFKCheckTrigger.
11945 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11946 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11947 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11948 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11951 tgCopyTuple
= heap_copytuple(tgtuple
);
11952 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11954 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11955 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11956 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11958 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11960 heap_freetuple(tgCopyTuple
);
11963 systable_endscan(tgscan
);
11967 * If the table at either end of the constraint is partitioned, we need to
11968 * recurse and handle every constraint that is a child of this one.
11970 * (This assumes that the recurse flag is forcibly set for partitioned
11971 * tables, and not set for legacy inheritance, though we don't check for
11974 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11975 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11979 HeapTuple childtup
;
11982 Anum_pg_constraint_conparentid
,
11983 BTEqualStrategyNumber
, F_OIDEQ
,
11984 ObjectIdGetDatum(conoid
));
11986 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11987 true, NULL
, 1, &pkey
);
11989 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11991 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11994 childrel
= table_open(childcon
->conrelid
, lockmode
);
11995 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11996 otherrelids
, lockmode
);
11997 table_close(childrel
, NoLock
);
12000 systable_endscan(pscan
);
12007 * ALTER TABLE VALIDATE CONSTRAINT
12009 * XXX The reason we handle recursion here rather than at Phase 1 is because
12010 * there's no good way to skip recursing when handling foreign keys: there is
12011 * no need to lock children in that case, yet we wouldn't be able to avoid
12012 * doing so at that level.
12014 * Return value is the address of the validated constraint. If the constraint
12015 * was already validated, InvalidObjectAddress is returned.
12017 static ObjectAddress
12018 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
12019 bool recurse
, bool recursing
, LOCKMODE lockmode
)
12023 ScanKeyData skey
[3];
12025 Form_pg_constraint con
;
12026 ObjectAddress address
;
12028 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12031 * Find and check the target constraint
12033 ScanKeyInit(&skey
[0],
12034 Anum_pg_constraint_conrelid
,
12035 BTEqualStrategyNumber
, F_OIDEQ
,
12036 ObjectIdGetDatum(RelationGetRelid(rel
)));
12037 ScanKeyInit(&skey
[1],
12038 Anum_pg_constraint_contypid
,
12039 BTEqualStrategyNumber
, F_OIDEQ
,
12040 ObjectIdGetDatum(InvalidOid
));
12041 ScanKeyInit(&skey
[2],
12042 Anum_pg_constraint_conname
,
12043 BTEqualStrategyNumber
, F_NAMEEQ
,
12044 CStringGetDatum(constrName
));
12045 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12046 true, NULL
, 3, skey
);
12048 /* There can be at most one matching row */
12049 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12051 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12052 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12053 constrName
, RelationGetRelationName(rel
))));
12055 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
12056 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
12057 con
->contype
!= CONSTRAINT_CHECK
)
12059 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12060 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12061 constrName
, RelationGetRelationName(rel
))));
12063 if (!con
->convalidated
)
12065 AlteredTableInfo
*tab
;
12066 HeapTuple copyTuple
;
12067 Form_pg_constraint copy_con
;
12069 if (con
->contype
== CONSTRAINT_FOREIGN
)
12071 NewConstraint
*newcon
;
12072 Constraint
*fkconstraint
;
12074 /* Queue validation for phase 3 */
12075 fkconstraint
= makeNode(Constraint
);
12076 /* for now this is all we need */
12077 fkconstraint
->conname
= constrName
;
12079 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
12080 newcon
->name
= constrName
;
12081 newcon
->contype
= CONSTR_FOREIGN
;
12082 newcon
->refrelid
= con
->confrelid
;
12083 newcon
->refindid
= con
->conindid
;
12084 newcon
->conid
= con
->oid
;
12085 newcon
->qual
= (Node
*) fkconstraint
;
12087 /* Find or create work queue entry for this table */
12088 tab
= ATGetQueueEntry(wqueue
, rel
);
12089 tab
->constraints
= lappend(tab
->constraints
, newcon
);
12092 * We disallow creating invalid foreign keys to or from
12093 * partitioned tables, so ignoring the recursion bit is okay.
12096 else if (con
->contype
== CONSTRAINT_CHECK
)
12098 List
*children
= NIL
;
12100 NewConstraint
*newcon
;
12105 * If we're recursing, the parent has already done this, so skip
12106 * it. Also, if the constraint is a NO INHERIT constraint, we
12107 * shouldn't try to look for it in the children.
12109 if (!recursing
&& !con
->connoinherit
)
12110 children
= find_all_inheritors(RelationGetRelid(rel
),
12114 * For CHECK constraints, we must ensure that we only mark the
12115 * constraint as validated on the parent if it's already validated
12118 * We recurse before validating on the parent, to reduce risk of
12121 foreach(child
, children
)
12123 Oid childoid
= lfirst_oid(child
);
12126 if (childoid
== RelationGetRelid(rel
))
12130 * If we are told not to recurse, there had better not be any
12131 * child tables, because we can't mark the constraint on the
12132 * parent valid unless it is valid for all child tables.
12136 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12137 errmsg("constraint must be validated on child tables too")));
12139 /* find_all_inheritors already got lock */
12140 childrel
= table_open(childoid
, NoLock
);
12142 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
12144 table_close(childrel
, NoLock
);
12147 /* Queue validation for phase 3 */
12148 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
12149 newcon
->name
= constrName
;
12150 newcon
->contype
= CONSTR_CHECK
;
12151 newcon
->refrelid
= InvalidOid
;
12152 newcon
->refindid
= InvalidOid
;
12153 newcon
->conid
= con
->oid
;
12155 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
12156 Anum_pg_constraint_conbin
);
12157 conbin
= TextDatumGetCString(val
);
12158 newcon
->qual
= (Node
*) stringToNode(conbin
);
12160 /* Find or create work queue entry for this table */
12161 tab
= ATGetQueueEntry(wqueue
, rel
);
12162 tab
->constraints
= lappend(tab
->constraints
, newcon
);
12165 * Invalidate relcache so that others see the new validated
12168 CacheInvalidateRelcache(rel
);
12172 * Now update the catalog, while we have the door open.
12174 copyTuple
= heap_copytuple(tuple
);
12175 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
12176 copy_con
->convalidated
= true;
12177 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
12179 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
12181 heap_freetuple(copyTuple
);
12183 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
12186 address
= InvalidObjectAddress
; /* already validated */
12188 systable_endscan(scan
);
12190 table_close(conrel
, RowExclusiveLock
);
12197 * transformColumnNameList - transform list of column names
12199 * Lookup each name and return its attnum and, optionally, type OID
12201 * Note: the name of this function suggests that it's general-purpose,
12202 * but actually it's only used to look up names appearing in foreign-key
12203 * clauses. The error messages would need work to use it in other cases,
12204 * and perhaps the validity checks as well.
12207 transformColumnNameList(Oid relId
, List
*colList
,
12208 int16
*attnums
, Oid
*atttypids
)
12214 foreach(l
, colList
)
12216 char *attname
= strVal(lfirst(l
));
12217 HeapTuple atttuple
;
12218 Form_pg_attribute attform
;
12220 atttuple
= SearchSysCacheAttName(relId
, attname
);
12221 if (!HeapTupleIsValid(atttuple
))
12223 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12224 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12226 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
12227 if (attform
->attnum
< 0)
12229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12230 errmsg("system columns cannot be used in foreign keys")));
12231 if (attnum
>= INDEX_MAX_KEYS
)
12233 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
12234 errmsg("cannot have more than %d keys in a foreign key",
12236 attnums
[attnum
] = attform
->attnum
;
12237 if (atttypids
!= NULL
)
12238 atttypids
[attnum
] = attform
->atttypid
;
12239 ReleaseSysCache(atttuple
);
12247 * transformFkeyGetPrimaryKey -
12249 * Look up the names, attnums, and types of the primary key attributes
12250 * for the pkrel. Also return the index OID and index opclasses of the
12251 * index supporting the primary key. Also return whether the index has
12252 * WITHOUT OVERLAPS.
12254 * All parameters except pkrel are output parameters. Also, the function
12255 * return value is the number of attributes in the primary key.
12257 * Used when the column list in the REFERENCES specification is omitted.
12260 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
12261 List
**attnamelist
,
12262 int16
*attnums
, Oid
*atttypids
,
12263 Oid
*opclasses
, bool *pk_has_without_overlaps
)
12265 List
*indexoidlist
;
12266 ListCell
*indexoidscan
;
12267 HeapTuple indexTuple
= NULL
;
12268 Form_pg_index indexStruct
= NULL
;
12269 Datum indclassDatum
;
12270 oidvector
*indclass
;
12274 * Get the list of index OIDs for the table from the relcache, and look up
12275 * each one in the pg_index syscache until we find one marked primary key
12276 * (hopefully there isn't more than one such). Insist it's valid, too.
12278 *indexOid
= InvalidOid
;
12280 indexoidlist
= RelationGetIndexList(pkrel
);
12282 foreach(indexoidscan
, indexoidlist
)
12284 Oid indexoid
= lfirst_oid(indexoidscan
);
12286 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12287 if (!HeapTupleIsValid(indexTuple
))
12288 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12289 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12290 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
12293 * Refuse to use a deferrable primary key. This is per SQL spec,
12294 * and there would be a lot of interesting semantic problems if we
12295 * tried to allow it.
12297 if (!indexStruct
->indimmediate
)
12299 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12300 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12301 RelationGetRelationName(pkrel
))));
12303 *indexOid
= indexoid
;
12306 ReleaseSysCache(indexTuple
);
12309 list_free(indexoidlist
);
12312 * Check that we found it
12314 if (!OidIsValid(*indexOid
))
12316 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12317 errmsg("there is no primary key for referenced table \"%s\"",
12318 RelationGetRelationName(pkrel
))));
12320 /* Must get indclass the hard way */
12321 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12322 Anum_pg_index_indclass
);
12323 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12326 * Now build the list of PK attributes from the indkey definition (we
12327 * assume a primary key cannot have expressional elements)
12329 *attnamelist
= NIL
;
12330 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
12332 int pkattno
= indexStruct
->indkey
.values
[i
];
12334 attnums
[i
] = pkattno
;
12335 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
12336 opclasses
[i
] = indclass
->values
[i
];
12337 *attnamelist
= lappend(*attnamelist
,
12338 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
12341 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12343 ReleaseSysCache(indexTuple
);
12349 * transformFkeyCheckAttrs -
12351 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12352 * reference as part of a foreign key constraint.
12354 * Returns the OID of the unique index supporting the constraint and
12355 * populates the caller-provided 'opclasses' array with the opclasses
12356 * associated with the index columns. Also sets whether the index
12357 * uses WITHOUT OVERLAPS.
12359 * Raises an ERROR on validation failure.
12362 transformFkeyCheckAttrs(Relation pkrel
,
12363 int numattrs
, int16
*attnums
,
12364 bool with_period
, Oid
*opclasses
,
12365 bool *pk_has_without_overlaps
)
12367 Oid indexoid
= InvalidOid
;
12368 bool found
= false;
12369 bool found_deferrable
= false;
12370 List
*indexoidlist
;
12371 ListCell
*indexoidscan
;
12376 * Reject duplicate appearances of columns in the referenced-columns list.
12377 * Such a case is forbidden by the SQL standard, and even if we thought it
12378 * useful to allow it, there would be ambiguity about how to match the
12379 * list to unique indexes (in particular, it'd be unclear which index
12380 * opclass goes with which FK column).
12382 for (i
= 0; i
< numattrs
; i
++)
12384 for (j
= i
+ 1; j
< numattrs
; j
++)
12386 if (attnums
[i
] == attnums
[j
])
12388 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12389 errmsg("foreign key referenced-columns list must not contain duplicates")));
12394 * Get the list of index OIDs for the table from the relcache, and look up
12395 * each one in the pg_index syscache, and match unique indexes to the list
12396 * of attnums we are given.
12398 indexoidlist
= RelationGetIndexList(pkrel
);
12400 foreach(indexoidscan
, indexoidlist
)
12402 HeapTuple indexTuple
;
12403 Form_pg_index indexStruct
;
12405 indexoid
= lfirst_oid(indexoidscan
);
12406 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12407 if (!HeapTupleIsValid(indexTuple
))
12408 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12409 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12412 * Must have the right number of columns; must be unique (or if
12413 * temporal then exclusion instead) and not a partial index; forget it
12414 * if there are any expressions, too. Invalid indexes are out as well.
12416 if (indexStruct
->indnkeyatts
== numattrs
&&
12417 (with_period
? indexStruct
->indisexclusion
: indexStruct
->indisunique
) &&
12418 indexStruct
->indisvalid
&&
12419 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
12420 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
12422 Datum indclassDatum
;
12423 oidvector
*indclass
;
12425 /* Must get indclass the hard way */
12426 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12427 Anum_pg_index_indclass
);
12428 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12431 * The given attnum list may match the index columns in any order.
12432 * Check for a match, and extract the appropriate opclasses while
12435 * We know that attnums[] is duplicate-free per the test at the
12436 * start of this function, and we checked above that the number of
12437 * index columns agrees, so if we find a match for each attnums[]
12438 * entry then we must have a one-to-one match in some order.
12440 for (i
= 0; i
< numattrs
; i
++)
12443 for (j
= 0; j
< numattrs
; j
++)
12445 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
12447 opclasses
[i
] = indclass
->values
[j
];
12455 /* The last attribute in the index must be the PERIOD FK part */
12456 if (found
&& with_period
)
12458 int16 periodattnum
= attnums
[numattrs
- 1];
12460 found
= (periodattnum
== indexStruct
->indkey
.values
[numattrs
- 1]);
12464 * Refuse to use a deferrable unique/primary key. This is per SQL
12465 * spec, and there would be a lot of interesting semantic problems
12466 * if we tried to allow it.
12468 if (found
&& !indexStruct
->indimmediate
)
12471 * Remember that we found an otherwise matching index, so that
12472 * we can generate a more appropriate error message.
12474 found_deferrable
= true;
12478 /* We need to know whether the index has WITHOUT OVERLAPS */
12480 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12482 ReleaseSysCache(indexTuple
);
12489 if (found_deferrable
)
12491 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12492 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12493 RelationGetRelationName(pkrel
))));
12496 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12497 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12498 RelationGetRelationName(pkrel
))));
12501 list_free(indexoidlist
);
12509 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12510 * Caller has equal regard for binary coercibility and for an exact match.
12512 static CoercionPathType
12513 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
12515 CoercionPathType ret
;
12517 if (targetTypeId
== sourceTypeId
)
12519 ret
= COERCION_PATH_RELABELTYPE
;
12520 *funcid
= InvalidOid
;
12524 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
12525 COERCION_IMPLICIT
, funcid
);
12526 if (ret
== COERCION_PATH_NONE
)
12527 /* A previously-relied-upon cast is now gone. */
12528 elog(ERROR
, "could not find cast from %u to %u",
12529 sourceTypeId
, targetTypeId
);
12536 * Permissions checks on the referenced table for ADD FOREIGN KEY
12538 * Note: we have already checked that the user owns the referencing table,
12539 * else we'd have failed much earlier; no additional checks are needed for it.
12542 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
12544 Oid roleid
= GetUserId();
12545 AclResult aclresult
;
12548 /* Okay if we have relation-level REFERENCES permission */
12549 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
12551 if (aclresult
== ACLCHECK_OK
)
12553 /* Else we must have REFERENCES on each column */
12554 for (i
= 0; i
< natts
; i
++)
12556 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
12557 roleid
, ACL_REFERENCES
);
12558 if (aclresult
!= ACLCHECK_OK
)
12559 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
12560 RelationGetRelationName(rel
));
12565 * Scan the existing rows in a table to verify they meet a proposed FK
12568 * Caller must have opened and locked both relations appropriately.
12571 validateForeignKeyConstraint(char *conname
,
12578 TupleTableSlot
*slot
;
12579 TableScanDesc scan
;
12580 Trigger trig
= {0};
12582 MemoryContext oldcxt
;
12583 MemoryContext perTupCxt
;
12586 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
12589 * Build a trigger call structure; we'll need it either way.
12591 trig
.tgoid
= InvalidOid
;
12592 trig
.tgname
= conname
;
12593 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
12594 trig
.tgisinternal
= true;
12595 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
12596 trig
.tgconstrindid
= pkindOid
;
12597 trig
.tgconstraint
= constraintOid
;
12598 trig
.tgdeferrable
= false;
12599 trig
.tginitdeferred
= false;
12600 /* we needn't fill in remaining fields */
12603 * See if we can do it with a single LEFT JOIN query. A false result
12604 * indicates we must proceed with the fire-the-trigger method. We can't do
12605 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12608 if (!hasperiod
&& RI_Initial_Check(&trig
, rel
, pkrel
))
12612 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12613 * if that tuple had just been inserted. If any of those fail, it should
12614 * ereport(ERROR) and that's that.
12616 snapshot
= RegisterSnapshot(GetLatestSnapshot());
12617 slot
= table_slot_create(rel
, NULL
);
12618 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
12620 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
12621 "validateForeignKeyConstraint",
12622 ALLOCSET_SMALL_SIZES
);
12623 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
12625 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
12627 LOCAL_FCINFO(fcinfo
, 0);
12628 TriggerData trigdata
= {0};
12630 CHECK_FOR_INTERRUPTS();
12633 * Make a call to the trigger function
12635 * No parameters are passed, but we do set a context
12637 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
12640 * We assume RI_FKey_check_ins won't look at flinfo...
12642 trigdata
.type
= T_TriggerData
;
12643 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
12644 trigdata
.tg_relation
= rel
;
12645 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
12646 trigdata
.tg_trigslot
= slot
;
12647 trigdata
.tg_trigger
= &trig
;
12649 fcinfo
->context
= (Node
*) &trigdata
;
12651 RI_FKey_check_ins(fcinfo
);
12653 MemoryContextReset(perTupCxt
);
12656 MemoryContextSwitchTo(oldcxt
);
12657 MemoryContextDelete(perTupCxt
);
12658 table_endscan(scan
);
12659 UnregisterSnapshot(snapshot
);
12660 ExecDropSingleTupleTableSlot(slot
);
12664 * CreateFKCheckTrigger
12665 * Creates the insert (on_insert=true) or update "check" trigger that
12666 * implements a given foreign key
12668 * Returns the OID of the so created trigger.
12671 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
12672 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
12675 ObjectAddress trigAddress
;
12676 CreateTrigStmt
*fk_trigger
;
12679 * Note: for a self-referential FK (referencing and referenced tables are
12680 * the same), it is important that the ON UPDATE action fires before the
12681 * CHECK action, since both triggers will fire on the same row during an
12682 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12683 * state of the row. Triggers fire in name order, so we ensure this by
12684 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12685 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12687 fk_trigger
= makeNode(CreateTrigStmt
);
12688 fk_trigger
->replace
= false;
12689 fk_trigger
->isconstraint
= true;
12690 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
12691 fk_trigger
->relation
= NULL
;
12693 /* Either ON INSERT or ON UPDATE */
12696 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
12697 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
12701 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
12702 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12705 fk_trigger
->args
= NIL
;
12706 fk_trigger
->row
= true;
12707 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12708 fk_trigger
->columns
= NIL
;
12709 fk_trigger
->whenClause
= NULL
;
12710 fk_trigger
->transitionRels
= NIL
;
12711 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12712 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12713 fk_trigger
->constrrel
= NULL
;
12715 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
12716 constraintOid
, indexOid
, InvalidOid
,
12717 parentTrigOid
, NULL
, true, false);
12719 /* Make changes-so-far visible */
12720 CommandCounterIncrement();
12722 return trigAddress
.objectId
;
12726 * createForeignKeyActionTriggers
12727 * Create the referenced-side "action" triggers that implement a foreign
12730 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12734 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
12735 Oid constraintOid
, Oid indexOid
,
12736 Oid parentDelTrigger
, Oid parentUpdTrigger
,
12737 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
12739 CreateTrigStmt
*fk_trigger
;
12740 ObjectAddress trigAddress
;
12743 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12744 * DELETE action on the referenced table.
12746 fk_trigger
= makeNode(CreateTrigStmt
);
12747 fk_trigger
->replace
= false;
12748 fk_trigger
->isconstraint
= true;
12749 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12750 fk_trigger
->relation
= NULL
;
12751 fk_trigger
->args
= NIL
;
12752 fk_trigger
->row
= true;
12753 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12754 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
12755 fk_trigger
->columns
= NIL
;
12756 fk_trigger
->whenClause
= NULL
;
12757 fk_trigger
->transitionRels
= NIL
;
12758 fk_trigger
->constrrel
= NULL
;
12760 switch (fkconstraint
->fk_del_action
)
12762 case FKCONSTR_ACTION_NOACTION
:
12763 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12764 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12765 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
12767 case FKCONSTR_ACTION_RESTRICT
:
12768 fk_trigger
->deferrable
= false;
12769 fk_trigger
->initdeferred
= false;
12770 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
12772 case FKCONSTR_ACTION_CASCADE
:
12773 fk_trigger
->deferrable
= false;
12774 fk_trigger
->initdeferred
= false;
12775 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
12777 case FKCONSTR_ACTION_SETNULL
:
12778 fk_trigger
->deferrable
= false;
12779 fk_trigger
->initdeferred
= false;
12780 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
12782 case FKCONSTR_ACTION_SETDEFAULT
:
12783 fk_trigger
->deferrable
= false;
12784 fk_trigger
->initdeferred
= false;
12785 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
12788 elog(ERROR
, "unrecognized FK action type: %d",
12789 (int) fkconstraint
->fk_del_action
);
12793 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12794 RelationGetRelid(rel
),
12795 constraintOid
, indexOid
, InvalidOid
,
12796 parentDelTrigger
, NULL
, true, false);
12798 *deleteTrigOid
= trigAddress
.objectId
;
12800 /* Make changes-so-far visible */
12801 CommandCounterIncrement();
12804 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12805 * UPDATE action on the referenced table.
12807 fk_trigger
= makeNode(CreateTrigStmt
);
12808 fk_trigger
->replace
= false;
12809 fk_trigger
->isconstraint
= true;
12810 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12811 fk_trigger
->relation
= NULL
;
12812 fk_trigger
->args
= NIL
;
12813 fk_trigger
->row
= true;
12814 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12815 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12816 fk_trigger
->columns
= NIL
;
12817 fk_trigger
->whenClause
= NULL
;
12818 fk_trigger
->transitionRels
= NIL
;
12819 fk_trigger
->constrrel
= NULL
;
12821 switch (fkconstraint
->fk_upd_action
)
12823 case FKCONSTR_ACTION_NOACTION
:
12824 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12825 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12826 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
12828 case FKCONSTR_ACTION_RESTRICT
:
12829 fk_trigger
->deferrable
= false;
12830 fk_trigger
->initdeferred
= false;
12831 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
12833 case FKCONSTR_ACTION_CASCADE
:
12834 fk_trigger
->deferrable
= false;
12835 fk_trigger
->initdeferred
= false;
12836 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
12838 case FKCONSTR_ACTION_SETNULL
:
12839 fk_trigger
->deferrable
= false;
12840 fk_trigger
->initdeferred
= false;
12841 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
12843 case FKCONSTR_ACTION_SETDEFAULT
:
12844 fk_trigger
->deferrable
= false;
12845 fk_trigger
->initdeferred
= false;
12846 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
12849 elog(ERROR
, "unrecognized FK action type: %d",
12850 (int) fkconstraint
->fk_upd_action
);
12854 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12855 RelationGetRelid(rel
),
12856 constraintOid
, indexOid
, InvalidOid
,
12857 parentUpdTrigger
, NULL
, true, false);
12859 *updateTrigOid
= trigAddress
.objectId
;
12863 * createForeignKeyCheckTriggers
12864 * Create the referencing-side "check" triggers that implement a foreign
12867 * Returns the OIDs of the so created triggers in *insertTrigOid and
12871 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
12872 Constraint
*fkconstraint
, Oid constraintOid
,
12874 Oid parentInsTrigger
, Oid parentUpdTrigger
,
12875 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
12877 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12878 constraintOid
, indexOid
,
12879 parentInsTrigger
, true);
12880 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12881 constraintOid
, indexOid
,
12882 parentUpdTrigger
, false);
12886 * ALTER TABLE DROP CONSTRAINT
12888 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12891 ATExecDropConstraint(Relation rel
, const char *constrName
,
12892 DropBehavior behavior
, bool recurse
,
12893 bool missing_ok
, LOCKMODE lockmode
)
12897 ScanKeyData skey
[3];
12899 bool found
= false;
12901 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12904 * Find and drop the target constraint
12906 ScanKeyInit(&skey
[0],
12907 Anum_pg_constraint_conrelid
,
12908 BTEqualStrategyNumber
, F_OIDEQ
,
12909 ObjectIdGetDatum(RelationGetRelid(rel
)));
12910 ScanKeyInit(&skey
[1],
12911 Anum_pg_constraint_contypid
,
12912 BTEqualStrategyNumber
, F_OIDEQ
,
12913 ObjectIdGetDatum(InvalidOid
));
12914 ScanKeyInit(&skey
[2],
12915 Anum_pg_constraint_conname
,
12916 BTEqualStrategyNumber
, F_NAMEEQ
,
12917 CStringGetDatum(constrName
));
12918 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12919 true, NULL
, 3, skey
);
12921 /* There can be at most one matching row */
12922 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12924 List
*readyRels
= NIL
;
12926 dropconstraint_internal(rel
, tuple
, behavior
, recurse
, false,
12927 missing_ok
, &readyRels
, lockmode
);
12931 systable_endscan(scan
);
12937 errcode(ERRCODE_UNDEFINED_OBJECT
),
12938 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12939 constrName
, RelationGetRelationName(rel
)));
12942 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12943 constrName
, RelationGetRelationName(rel
)));
12946 table_close(conrel
, RowExclusiveLock
);
12950 * Remove a constraint, using its pg_constraint tuple
12952 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12955 * Returns the address of the constraint being removed.
12957 static ObjectAddress
12958 dropconstraint_internal(Relation rel
, HeapTuple constraintTup
, DropBehavior behavior
,
12959 bool recurse
, bool recursing
, bool missing_ok
, List
**readyRels
,
12963 Form_pg_constraint con
;
12964 ObjectAddress conobj
;
12966 bool is_no_inherit_constraint
= false;
12968 List
*unconstrained_cols
= NIL
;
12969 char *colname
= NULL
;
12970 bool dropping_pk
= false;
12972 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
12973 return InvalidObjectAddress
;
12974 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
12976 /* Guard against stack overflow due to overly deep inheritance tree. */
12977 check_stack_depth();
12979 /* At top level, permission check was done in ATPrepCmd, else do it */
12981 ATSimplePermissions(AT_DropConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
12983 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12985 con
= (Form_pg_constraint
) GETSTRUCT(constraintTup
);
12986 constrName
= NameStr(con
->conname
);
12989 * If we're asked to drop a constraint which is both defined locally and
12990 * inherited, we can simply mark it as no longer having a local
12991 * definition, and no further changes are required.
12993 * XXX We do this for not-null constraints only, not CHECK, because the
12994 * latter have historically not behaved this way and it might be confusing
12995 * to change the behavior now.
12997 if (con
->contype
== CONSTRAINT_NOTNULL
&&
12998 con
->conislocal
&& con
->coninhcount
> 0)
13002 copytup
= heap_copytuple(constraintTup
);
13003 con
= (Form_pg_constraint
) GETSTRUCT(copytup
);
13004 con
->conislocal
= false;
13005 CatalogTupleUpdate(conrel
, ©tup
->t_self
, copytup
);
13006 ObjectAddressSet(conobj
, ConstraintRelationId
, con
->oid
);
13008 CommandCounterIncrement();
13009 table_close(conrel
, RowExclusiveLock
);
13013 /* Don't allow drop of inherited constraints */
13014 if (con
->coninhcount
> 0 && !recursing
)
13016 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13017 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13018 constrName
, RelationGetRelationName(rel
))));
13021 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
13022 * more checks and actions below, so obtain the list of columns that are
13023 * constrained by the constraint being dropped.
13025 if (con
->contype
== CONSTRAINT_NOTNULL
)
13029 colnum
= extractNotNullColumn(constraintTup
);
13030 unconstrained_cols
= list_make1_int(colnum
);
13031 colname
= NameStr(TupleDescAttr(RelationGetDescr(rel
),
13032 colnum
- 1)->attname
);
13034 else if (con
->contype
== CONSTRAINT_PRIMARY
)
13042 dropping_pk
= true;
13044 adatum
= heap_getattr(constraintTup
, Anum_pg_constraint_conkey
,
13045 RelationGetDescr(conrel
), &isNull
);
13047 elog(ERROR
, "null conkey for constraint %u", con
->oid
);
13048 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
13049 numkeys
= ARR_DIMS(arr
)[0];
13050 if (ARR_NDIM(arr
) != 1 ||
13052 ARR_HASNULL(arr
) ||
13053 ARR_ELEMTYPE(arr
) != INT2OID
)
13054 elog(ERROR
, "conkey is not a 1-D smallint array");
13055 attnums
= (int16
*) ARR_DATA_PTR(arr
);
13057 for (int i
= 0; i
< numkeys
; i
++)
13058 unconstrained_cols
= lappend_int(unconstrained_cols
, attnums
[i
]);
13061 is_no_inherit_constraint
= con
->connoinherit
;
13064 * If it's a foreign-key constraint, we'd better lock the referenced table
13065 * and check that that's not in use, just as we've already done for the
13066 * constrained table (else we might, eg, be dropping a trigger that has
13067 * unfired events). But we can/must skip that in the self-referential
13070 if (con
->contype
== CONSTRAINT_FOREIGN
&&
13071 con
->confrelid
!= RelationGetRelid(rel
))
13075 /* Must match lock taken by RemoveTriggerById: */
13076 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
13077 CheckTableNotInUse(frel
, "ALTER TABLE");
13078 table_close(frel
, NoLock
);
13082 * Perform the actual constraint deletion
13084 ObjectAddressSet(conobj
, ConstraintRelationId
, con
->oid
);
13085 performDeletion(&conobj
, behavior
, 0);
13088 * If this was a NOT NULL or the primary key, verify that we still have
13089 * constraints to support GENERATED AS IDENTITY or the replica identity.
13091 if (unconstrained_cols
!= NIL
)
13097 /* Make implicit attnotnull changes visible */
13098 CommandCounterIncrement();
13100 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
13103 * We want to test columns for their presence in the primary key, but
13104 * only if we're not dropping it.
13106 pkcols
= dropping_pk
? NULL
:
13107 RelationGetIndexAttrBitmap(rel
,
13108 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
13109 ircols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_IDENTITY_KEY
);
13111 foreach_int(attnum
, unconstrained_cols
)
13115 Form_pg_attribute attForm
;
13119 * Obtain pg_attribute tuple and verify conditions on it.
13121 atttup
= SearchSysCacheAttNum(RelationGetRelid(rel
), attnum
);
13122 if (!HeapTupleIsValid(atttup
))
13123 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
13124 attnum
, RelationGetRelid(rel
));
13125 attForm
= (Form_pg_attribute
) GETSTRUCT(atttup
);
13126 attidentity
= attForm
->attidentity
;
13127 ReleaseSysCache(atttup
);
13130 * Since the above deletion has been made visible, we can now
13131 * search for any remaining constraints on this column (or these
13132 * columns, in the case we're dropping a multicol primary key.)
13133 * Then, verify whether any further NOT NULL or primary key
13134 * exists: if none and this is a generated identity column or the
13135 * replica identity, abort the whole thing.
13137 contup
= findNotNullConstraintAttnum(RelationGetRelid(rel
), attnum
);
13139 bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
13144 * It's not valid to drop the not-null constraint for a GENERATED
13145 * AS IDENTITY column.
13147 if (attidentity
!= '\0')
13149 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
13150 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13151 get_attname(RelationGetRelid(rel
), attnum
,
13153 RelationGetRelationName(rel
)));
13156 * It's not valid to drop the not-null constraint for a column in
13157 * the replica identity index, either. (FULL is not affected.)
13159 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
, ircols
))
13161 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13162 errmsg("column \"%s\" is in index used as replica identity",
13163 get_attname(RelationGetRelid(rel
), attnum
, false)));
13165 table_close(attrel
, RowExclusiveLock
);
13169 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13170 * are dropped via the dependency mechanism, so we're done here.
13172 if (con
->contype
!= CONSTRAINT_CHECK
&&
13173 con
->contype
!= CONSTRAINT_NOTNULL
&&
13174 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
13176 table_close(conrel
, RowExclusiveLock
);
13181 * Propagate to children as appropriate. Unlike most other ALTER
13182 * routines, we have to do this one level of recursion at a time; we can't
13183 * use find_all_inheritors to do it in one pass.
13185 if (!is_no_inherit_constraint
)
13186 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
13191 * For a partitioned table, if partitions exist and we are told not to
13192 * recurse, it's a user error. It doesn't make sense to have a constraint
13193 * be defined only on the parent, especially if it's a partitioned table.
13195 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
13196 children
!= NIL
&& !recurse
)
13198 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13199 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13200 errhint("Do not specify the ONLY keyword.")));
13202 foreach_oid(childrelid
, children
)
13206 Form_pg_constraint childcon
;
13208 if (list_member_oid(*readyRels
, childrelid
))
13209 continue; /* child already processed */
13211 /* find_inheritance_children already got lock */
13212 childrel
= table_open(childrelid
, NoLock
);
13213 CheckTableNotInUse(childrel
, "ALTER TABLE");
13216 * We search for not-null constraints by column name, and others by
13219 if (con
->contype
== CONSTRAINT_NOTNULL
)
13221 tuple
= findNotNullConstraint(childrelid
, colname
);
13222 if (!HeapTupleIsValid(tuple
))
13223 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13224 colname
, RelationGetRelid(childrel
));
13229 ScanKeyData skey
[3];
13231 ScanKeyInit(&skey
[0],
13232 Anum_pg_constraint_conrelid
,
13233 BTEqualStrategyNumber
, F_OIDEQ
,
13234 ObjectIdGetDatum(childrelid
));
13235 ScanKeyInit(&skey
[1],
13236 Anum_pg_constraint_contypid
,
13237 BTEqualStrategyNumber
, F_OIDEQ
,
13238 ObjectIdGetDatum(InvalidOid
));
13239 ScanKeyInit(&skey
[2],
13240 Anum_pg_constraint_conname
,
13241 BTEqualStrategyNumber
, F_NAMEEQ
,
13242 CStringGetDatum(constrName
));
13243 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
13244 true, NULL
, 3, skey
);
13245 /* There can only be one, so no need to loop */
13246 tuple
= systable_getnext(scan
);
13247 if (!HeapTupleIsValid(tuple
))
13249 (errcode(ERRCODE_UNDEFINED_OBJECT
),
13250 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13252 RelationGetRelationName(childrel
))));
13253 tuple
= heap_copytuple(tuple
);
13254 systable_endscan(scan
);
13257 childcon
= (Form_pg_constraint
) GETSTRUCT(tuple
);
13259 /* Right now only CHECK and not-null constraints can be inherited */
13260 if (childcon
->contype
!= CONSTRAINT_CHECK
&&
13261 childcon
->contype
!= CONSTRAINT_NOTNULL
)
13262 elog(ERROR
, "inherited constraint is not a CHECK or not-null constraint");
13264 if (childcon
->coninhcount
<= 0) /* shouldn't happen */
13265 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
13266 childrelid
, NameStr(childcon
->conname
));
13271 * If the child constraint has other definition sources, just
13272 * decrement its inheritance count; if not, recurse to delete it.
13274 if (childcon
->coninhcount
== 1 && !childcon
->conislocal
)
13276 /* Time to delete this child constraint, too */
13277 dropconstraint_internal(childrel
, tuple
, behavior
,
13278 recurse
, true, missing_ok
, readyRels
,
13283 /* Child constraint must survive my deletion */
13284 childcon
->coninhcount
--;
13285 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13287 /* Make update visible */
13288 CommandCounterIncrement();
13294 * If we were told to drop ONLY in this table (no recursion) and
13295 * there are no further parents for this constraint, we need to
13296 * mark the inheritors' constraints as locally defined rather than
13299 childcon
->coninhcount
--;
13300 if (childcon
->coninhcount
== 0)
13301 childcon
->conislocal
= true;
13303 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13305 /* Make update visible */
13306 CommandCounterIncrement();
13309 heap_freetuple(tuple
);
13311 table_close(childrel
, NoLock
);
13315 * In addition, when dropping a primary key from a legacy-inheritance
13316 * parent table, we must recurse to children to mark the corresponding NOT
13317 * NULL constraint as no longer inherited, or drop it if this its last
13320 if (con
->contype
== CONSTRAINT_PRIMARY
&&
13321 rel
->rd_rel
->relkind
== RELKIND_RELATION
&&
13322 rel
->rd_rel
->relhassubclass
)
13324 List
*colnames
= NIL
;
13325 List
*pkready
= NIL
;
13328 * Because primary keys are always marked as NO INHERIT, we don't have
13329 * a list of children yet, so obtain one now.
13331 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
13334 * Find out the list of column names to process. Fortunately, we
13335 * already have the list of column numbers.
13337 foreach_int(attnum
, unconstrained_cols
)
13339 colnames
= lappend(colnames
, get_attname(RelationGetRelid(rel
),
13343 foreach_oid(childrelid
, children
)
13347 if (list_member_oid(pkready
, childrelid
))
13348 continue; /* child already processed */
13350 /* find_inheritance_children already got lock */
13351 childrel
= table_open(childrelid
, NoLock
);
13352 CheckTableNotInUse(childrel
, "ALTER TABLE");
13354 foreach_ptr(char, colName
, colnames
)
13358 contup
= findNotNullConstraint(childrelid
, colName
);
13359 if (contup
== NULL
)
13360 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13361 colName
, RelationGetRelationName(childrel
));
13363 dropconstraint_internal(childrel
, contup
,
13364 DROP_RESTRICT
, true, true,
13370 table_close(childrel
, NoLock
);
13372 pkready
= lappend_oid(pkready
, childrelid
);
13376 table_close(conrel
, RowExclusiveLock
);
13382 * ALTER COLUMN TYPE
13384 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13385 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13386 * transformed (and must be, because we rely on some transformed fields).
13388 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13389 * table will be done "in parallel" during phase 3, so all the USING
13390 * expressions should be parsed assuming the original column types. Also,
13391 * this allows a USING expression to refer to a field that will be dropped.
13393 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13394 * the first two execution steps in phase 2; they must not see the effects
13395 * of any other subcommand types, since the USING expressions are parsed
13396 * against the unmodified table's state.
13399 ATPrepAlterColumnType(List
**wqueue
,
13400 AlteredTableInfo
*tab
, Relation rel
,
13401 bool recurse
, bool recursing
,
13402 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
13403 AlterTableUtilityContext
*context
)
13405 char *colName
= cmd
->name
;
13406 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13407 TypeName
*typeName
= def
->typeName
;
13408 Node
*transform
= def
->cooked_default
;
13410 Form_pg_attribute attTup
;
13413 int32 targettypmod
;
13415 NewColumnValue
*newval
;
13416 ParseState
*pstate
= make_parsestate(NULL
);
13417 AclResult aclresult
;
13420 if (rel
->rd_rel
->reloftype
&& !recursing
)
13422 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13423 errmsg("cannot alter column type of typed table")));
13425 /* lookup the attribute so we can check inheritance status */
13426 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
13427 if (!HeapTupleIsValid(tuple
))
13429 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13430 errmsg("column \"%s\" of relation \"%s\" does not exist",
13431 colName
, RelationGetRelationName(rel
))));
13432 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
13433 attnum
= attTup
->attnum
;
13435 /* Can't alter a system attribute */
13438 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13439 errmsg("cannot alter system column \"%s\"",
13443 * Don't alter inherited columns. At outer level, there had better not be
13444 * any inherited definition; when recursing, we assume this was checked at
13445 * the parent level (see below).
13447 if (attTup
->attinhcount
> 0 && !recursing
)
13449 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13450 errmsg("cannot alter inherited column \"%s\"",
13453 /* Don't alter columns used in the partition key */
13454 if (has_partition_attrs(rel
,
13455 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
13458 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13459 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13460 colName
, RelationGetRelationName(rel
))));
13462 /* Look up the target type */
13463 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
13465 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
13466 if (aclresult
!= ACLCHECK_OK
)
13467 aclcheck_error_type(aclresult
, targettype
);
13469 /* And the collation */
13470 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13472 /* make sure datatype is legal for a column */
13473 CheckAttributeType(colName
, targettype
, targetcollid
,
13474 list_make1_oid(rel
->rd_rel
->reltype
),
13477 if (tab
->relkind
== RELKIND_RELATION
||
13478 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
13481 * Set up an expression to transform the old data value to the new
13482 * type. If a USING option was given, use the expression as
13483 * transformed by transformAlterTableStmt, else just take the old
13484 * value and try to coerce it. We do this first so that type
13485 * incompatibility can be detected before we waste effort, and because
13486 * we need the expression to be parsed against the original table row
13491 transform
= (Node
*) makeVar(1, attnum
,
13492 attTup
->atttypid
, attTup
->atttypmod
,
13493 attTup
->attcollation
,
13497 transform
= coerce_to_target_type(pstate
,
13498 transform
, exprType(transform
),
13499 targettype
, targettypmod
,
13500 COERCION_ASSIGNMENT
,
13501 COERCE_IMPLICIT_CAST
,
13503 if (transform
== NULL
)
13505 /* error text depends on whether USING was specified or not */
13506 if (def
->cooked_default
!= NULL
)
13508 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13509 errmsg("result of USING clause for column \"%s\""
13510 " cannot be cast automatically to type %s",
13511 colName
, format_type_be(targettype
)),
13512 errhint("You might need to add an explicit cast.")));
13515 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13516 errmsg("column \"%s\" cannot be cast automatically to type %s",
13517 colName
, format_type_be(targettype
)),
13518 /* translator: USING is SQL, don't translate it */
13519 errhint("You might need to specify \"USING %s::%s\".",
13520 quote_identifier(colName
),
13521 format_type_with_typemod(targettype
,
13525 /* Fix collations after all else */
13526 assign_expr_collations(pstate
, transform
);
13528 /* Plan the expr now so we can accurately assess the need to rewrite. */
13529 transform
= (Node
*) expression_planner((Expr
*) transform
);
13532 * Add a work queue item to make ATRewriteTable update the column
13535 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
13536 newval
->attnum
= attnum
;
13537 newval
->expr
= (Expr
*) transform
;
13538 newval
->is_generated
= false;
13540 tab
->newvals
= lappend(tab
->newvals
, newval
);
13541 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
13542 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
13544 else if (transform
)
13546 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13547 errmsg("\"%s\" is not a table",
13548 RelationGetRelationName(rel
))));
13550 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
13553 * For relations without storage, do this check now. Regular tables
13554 * will check it later when the table is being rewritten.
13556 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
13559 ReleaseSysCache(tuple
);
13562 * Recurse manually by queueing a new command for each child, if
13563 * necessary. We cannot apply ATSimpleRecursion here because we need to
13564 * remap attribute numbers in the USING expression, if any.
13566 * If we are told not to recurse, there had better not be any child
13567 * tables; else the alter would put them out of step.
13571 Oid relid
= RelationGetRelid(rel
);
13577 child_oids
= find_all_inheritors(relid
, lockmode
,
13578 &child_numparents
);
13581 * find_all_inheritors does the recursive search of the inheritance
13582 * hierarchy, so all we have to do is process all of the relids in the
13583 * list that it returns.
13585 forboth(lo
, child_oids
, li
, child_numparents
)
13587 Oid childrelid
= lfirst_oid(lo
);
13588 int numparents
= lfirst_int(li
);
13590 HeapTuple childtuple
;
13591 Form_pg_attribute childattTup
;
13593 if (childrelid
== relid
)
13596 /* find_all_inheritors already got lock */
13597 childrel
= relation_open(childrelid
, NoLock
);
13598 CheckTableNotInUse(childrel
, "ALTER TABLE");
13601 * Verify that the child doesn't have any inherited definitions of
13602 * this column that came from outside this inheritance hierarchy.
13603 * (renameatt makes a similar test, though in a different way
13604 * because of its different recursion mechanism.)
13606 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
13608 if (!HeapTupleIsValid(childtuple
))
13610 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13611 errmsg("column \"%s\" of relation \"%s\" does not exist",
13612 colName
, RelationGetRelationName(childrel
))));
13613 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
13615 if (childattTup
->attinhcount
> numparents
)
13617 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13618 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13619 colName
, RelationGetRelationName(childrel
))));
13621 ReleaseSysCache(childtuple
);
13624 * Remap the attribute numbers. If no USING expression was
13625 * specified, there is no need for this step.
13627 if (def
->cooked_default
)
13630 bool found_whole_row
;
13632 /* create a copy to scribble on */
13633 cmd
= copyObject(cmd
);
13635 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
13636 RelationGetDescr(rel
),
13638 ((ColumnDef
*) cmd
->def
)->cooked_default
=
13639 map_variable_attnos(def
->cooked_default
,
13642 InvalidOid
, &found_whole_row
);
13643 if (found_whole_row
)
13645 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13646 errmsg("cannot convert whole-row table reference"),
13647 errdetail("USING expression contains a whole-row table reference.")));
13650 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
13651 relation_close(childrel
, NoLock
);
13654 else if (!recursing
&&
13655 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
13657 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13658 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13661 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
13662 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
13666 * When the data type of a column is changed, a rewrite might not be required
13667 * if the new type is sufficiently identical to the old one, and the USING
13668 * clause isn't trying to insert some other value. It's safe to skip the
13669 * rewrite in these cases:
13671 * - the old type is binary coercible to the new type
13672 * - the new type is an unconstrained domain over the old type
13673 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13675 * In the case of a constrained domain, we could get by with scanning the
13676 * table and checking the constraint rather than actually rewriting it, but we
13677 * don't currently try to do that.
13680 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
13682 Assert(expr
!= NULL
);
13686 /* only one varno, so no need to check that */
13687 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
13689 else if (IsA(expr
, RelabelType
))
13690 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
13691 else if (IsA(expr
, CoerceToDomain
))
13693 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
13695 if (DomainHasConstraints(d
->resulttype
))
13697 expr
= (Node
*) d
->arg
;
13699 else if (IsA(expr
, FuncExpr
))
13701 FuncExpr
*f
= (FuncExpr
*) expr
;
13705 case F_TIMESTAMPTZ_TIMESTAMP
:
13706 case F_TIMESTAMP_TIMESTAMPTZ
:
13707 if (TimestampTimestampTzRequiresRewrite())
13710 expr
= linitial(f
->args
);
13722 * ALTER COLUMN .. SET DATA TYPE
13724 * Return the address of the modified column.
13726 static ObjectAddress
13727 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
13728 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
13730 char *colName
= cmd
->name
;
13731 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13732 TypeName
*typeName
= def
->typeName
;
13734 Form_pg_attribute attTup
,
13737 HeapTuple typeTuple
;
13738 Form_pg_type tform
;
13740 int32 targettypmod
;
13743 Relation attrelation
;
13745 ScanKeyData key
[3];
13748 ObjectAddress address
;
13751 * Clear all the missing values if we're rewriting the table, since this
13752 * renders them pointless.
13758 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
13759 RelationClearMissing(newrel
);
13760 relation_close(newrel
, NoLock
);
13761 /* make sure we don't conflict with later attribute modifications */
13762 CommandCounterIncrement();
13765 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
13767 /* Look up the target column */
13768 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
13769 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
13771 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13772 errmsg("column \"%s\" of relation \"%s\" does not exist",
13773 colName
, RelationGetRelationName(rel
))));
13774 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13775 attnum
= attTup
->attnum
;
13776 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
13778 /* Check for multiple ALTER TYPE on same column --- can't cope */
13779 if (attTup
->atttypid
!= attOldTup
->atttypid
||
13780 attTup
->atttypmod
!= attOldTup
->atttypmod
)
13782 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13783 errmsg("cannot alter type of column \"%s\" twice",
13786 /* Look up the target type (should not fail, since prep found it) */
13787 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
13788 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
13789 targettype
= tform
->oid
;
13790 /* And the collation */
13791 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13794 * If there is a default expression for the column, get it and ensure we
13795 * can coerce it to the new datatype. (We must do this before changing
13796 * the column type, because build_column_default itself will try to
13797 * coerce, and will not issue the error message we want if it fails.)
13799 * We remove any implicit coercion steps at the top level of the old
13800 * default expression; this has been agreed to satisfy the principle of
13801 * least surprise. (The conversion to the new column type should act like
13802 * it started from what the user sees as the stored expression, and the
13803 * implicit coercions aren't going to be shown.)
13805 if (attTup
->atthasdef
)
13807 defaultexpr
= build_column_default(rel
, attnum
);
13808 Assert(defaultexpr
);
13809 defaultexpr
= strip_implicit_coercions(defaultexpr
);
13810 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
13811 defaultexpr
, exprType(defaultexpr
),
13812 targettype
, targettypmod
,
13813 COERCION_ASSIGNMENT
,
13814 COERCE_IMPLICIT_CAST
,
13816 if (defaultexpr
== NULL
)
13818 if (attTup
->attgenerated
)
13820 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13821 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13822 colName
, format_type_be(targettype
))));
13825 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13826 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13827 colName
, format_type_be(targettype
))));
13831 defaultexpr
= NULL
;
13834 * Find everything that depends on the column (constraints, indexes, etc),
13835 * and record enough information to let us recreate the objects.
13837 * The actual recreation does not happen here, but only after we have
13838 * performed all the individual ALTER TYPE operations. We have to save
13839 * the info before executing ALTER TYPE, though, else the deparser will
13842 RememberAllDependentForRebuilding(tab
, AT_AlterColumnType
, rel
, attnum
, colName
);
13845 * Now scan for dependencies of this column on other things. The only
13846 * things we should find are the dependency on the column datatype and
13847 * possibly a collation dependency. Those can be removed.
13849 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13851 ScanKeyInit(&key
[0],
13852 Anum_pg_depend_classid
,
13853 BTEqualStrategyNumber
, F_OIDEQ
,
13854 ObjectIdGetDatum(RelationRelationId
));
13855 ScanKeyInit(&key
[1],
13856 Anum_pg_depend_objid
,
13857 BTEqualStrategyNumber
, F_OIDEQ
,
13858 ObjectIdGetDatum(RelationGetRelid(rel
)));
13859 ScanKeyInit(&key
[2],
13860 Anum_pg_depend_objsubid
,
13861 BTEqualStrategyNumber
, F_INT4EQ
,
13862 Int32GetDatum((int32
) attnum
));
13864 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
13867 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13869 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13870 ObjectAddress foundObject
;
13872 foundObject
.classId
= foundDep
->refclassid
;
13873 foundObject
.objectId
= foundDep
->refobjid
;
13874 foundObject
.objectSubId
= foundDep
->refobjsubid
;
13876 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
13877 elog(ERROR
, "found unexpected dependency type '%c'",
13878 foundDep
->deptype
);
13879 if (!(foundDep
->refclassid
== TypeRelationId
&&
13880 foundDep
->refobjid
== attTup
->atttypid
) &&
13881 !(foundDep
->refclassid
== CollationRelationId
&&
13882 foundDep
->refobjid
== attTup
->attcollation
))
13883 elog(ERROR
, "found unexpected dependency for column: %s",
13884 getObjectDescription(&foundObject
, false));
13886 CatalogTupleDelete(depRel
, &depTup
->t_self
);
13889 systable_endscan(scan
);
13891 table_close(depRel
, RowExclusiveLock
);
13894 * Here we go --- change the recorded column type and collation. (Note
13895 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13896 * fix up the missing value if any.
13898 if (attTup
->atthasmissing
)
13903 /* if rewrite is true the missing value should already be cleared */
13904 Assert(tab
->rewrite
== 0);
13906 /* Get the missing value datum */
13907 missingval
= heap_getattr(heapTup
,
13908 Anum_pg_attribute_attmissingval
,
13909 attrelation
->rd_att
,
13912 /* if it's a null array there is nothing to do */
13917 * Get the datum out of the array and repack it in a new array
13918 * built with the new type data. We assume that since the table
13919 * doesn't need rewriting, the actual Datum doesn't need to be
13920 * changed, only the array metadata.
13925 Datum valuesAtt
[Natts_pg_attribute
] = {0};
13926 bool nullsAtt
[Natts_pg_attribute
] = {0};
13927 bool replacesAtt
[Natts_pg_attribute
] = {0};
13930 missingval
= array_get_element(missingval
,
13938 missingval
= PointerGetDatum(construct_array(&missingval
,
13945 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
13946 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
13947 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
13949 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
13950 valuesAtt
, nullsAtt
, replacesAtt
);
13951 heap_freetuple(heapTup
);
13953 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13957 attTup
->atttypid
= targettype
;
13958 attTup
->atttypmod
= targettypmod
;
13959 attTup
->attcollation
= targetcollid
;
13960 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
13962 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
13963 errmsg("too many array dimensions"));
13964 attTup
->attndims
= list_length(typeName
->arrayBounds
);
13965 attTup
->attlen
= tform
->typlen
;
13966 attTup
->attbyval
= tform
->typbyval
;
13967 attTup
->attalign
= tform
->typalign
;
13968 attTup
->attstorage
= tform
->typstorage
;
13969 attTup
->attcompression
= InvalidCompressionMethod
;
13971 ReleaseSysCache(typeTuple
);
13973 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
13975 table_close(attrelation
, RowExclusiveLock
);
13977 /* Install dependencies on new datatype and collation */
13978 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
13979 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
13982 * Drop any pg_statistic entry for the column, since it's now wrong type
13984 RemoveStatistics(RelationGetRelid(rel
), attnum
);
13986 InvokeObjectPostAlterHook(RelationRelationId
,
13987 RelationGetRelid(rel
), attnum
);
13990 * Update the default, if present, by brute force --- remove and re-add
13991 * the default. Probably unsafe to take shortcuts, since the new version
13992 * may well have additional dependencies. (It's okay to do this now,
13993 * rather than after other ALTER TYPE commands, since the default won't
13994 * depend on other column types.)
13999 * If it's a GENERATED default, drop its dependency records, in
14000 * particular its INTERNAL dependency on the column, which would
14001 * otherwise cause dependency.c to refuse to perform the deletion.
14003 if (attTup
->attgenerated
)
14005 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
14007 if (!OidIsValid(attrdefoid
))
14008 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
14009 RelationGetRelid(rel
), attnum
);
14010 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
14014 * Make updates-so-far visible, particularly the new pg_attribute row
14015 * which will be updated again.
14017 CommandCounterIncrement();
14020 * We use RESTRICT here for safety, but at present we do not expect
14021 * anything to depend on the default.
14023 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
14026 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
14029 ObjectAddressSubSet(address
, RelationRelationId
,
14030 RelationGetRelid(rel
), attnum
);
14033 heap_freetuple(heapTup
);
14039 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14040 * that depends on the column (constraints, indexes, etc), and record enough
14041 * information to let us recreate the objects.
14044 RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
14045 Relation rel
, AttrNumber attnum
, const char *colName
)
14048 ScanKeyData key
[3];
14052 Assert(subtype
== AT_AlterColumnType
|| subtype
== AT_SetExpression
);
14054 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
14056 ScanKeyInit(&key
[0],
14057 Anum_pg_depend_refclassid
,
14058 BTEqualStrategyNumber
, F_OIDEQ
,
14059 ObjectIdGetDatum(RelationRelationId
));
14060 ScanKeyInit(&key
[1],
14061 Anum_pg_depend_refobjid
,
14062 BTEqualStrategyNumber
, F_OIDEQ
,
14063 ObjectIdGetDatum(RelationGetRelid(rel
)));
14064 ScanKeyInit(&key
[2],
14065 Anum_pg_depend_refobjsubid
,
14066 BTEqualStrategyNumber
, F_INT4EQ
,
14067 Int32GetDatum((int32
) attnum
));
14069 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
14072 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
14074 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
14075 ObjectAddress foundObject
;
14077 foundObject
.classId
= foundDep
->classid
;
14078 foundObject
.objectId
= foundDep
->objid
;
14079 foundObject
.objectSubId
= foundDep
->objsubid
;
14081 switch (foundObject
.classId
)
14083 case RelationRelationId
:
14085 char relKind
= get_rel_relkind(foundObject
.objectId
);
14087 if (relKind
== RELKIND_INDEX
||
14088 relKind
== RELKIND_PARTITIONED_INDEX
)
14090 Assert(foundObject
.objectSubId
== 0);
14091 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
14093 else if (relKind
== RELKIND_SEQUENCE
)
14096 * This must be a SERIAL column's sequence. We need
14097 * not do anything to it.
14099 Assert(foundObject
.objectSubId
== 0);
14103 /* Not expecting any other direct dependencies... */
14104 elog(ERROR
, "unexpected object depending on column: %s",
14105 getObjectDescription(&foundObject
, false));
14110 case ConstraintRelationId
:
14111 Assert(foundObject
.objectSubId
== 0);
14112 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
14115 case ProcedureRelationId
:
14118 * A new-style SQL function can depend on a column, if that
14119 * column is referenced in the parsed function body. Ideally
14120 * we'd automatically update the function by deparsing and
14121 * reparsing it, but that's risky and might well fail anyhow.
14124 * This is only a problem for AT_AlterColumnType, not
14125 * AT_SetExpression.
14127 if (subtype
== AT_AlterColumnType
)
14129 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14130 errmsg("cannot alter type of a column used by a function or procedure"),
14131 errdetail("%s depends on column \"%s\"",
14132 getObjectDescription(&foundObject
, false),
14136 case RewriteRelationId
:
14139 * View/rule bodies have pretty much the same issues as
14140 * function bodies. FIXME someday.
14142 if (subtype
== AT_AlterColumnType
)
14144 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14145 errmsg("cannot alter type of a column used by a view or rule"),
14146 errdetail("%s depends on column \"%s\"",
14147 getObjectDescription(&foundObject
, false),
14151 case TriggerRelationId
:
14154 * A trigger can depend on a column because the column is
14155 * specified as an update target, or because the column is
14156 * used in the trigger's WHEN condition. The first case would
14157 * not require any extra work, but the second case would
14158 * require updating the WHEN expression, which has the same
14159 * issues as above. Since we can't easily tell which case
14160 * applies, we punt for both. FIXME someday.
14162 if (subtype
== AT_AlterColumnType
)
14164 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14165 errmsg("cannot alter type of a column used in a trigger definition"),
14166 errdetail("%s depends on column \"%s\"",
14167 getObjectDescription(&foundObject
, false),
14171 case PolicyRelationId
:
14174 * A policy can depend on a column because the column is
14175 * specified in the policy's USING or WITH CHECK qual
14176 * expressions. It might be possible to rewrite and recheck
14177 * the policy expression, but punt for now. It's certainly
14178 * easy enough to remove and recreate the policy; still, FIXME
14181 if (subtype
== AT_AlterColumnType
)
14183 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14184 errmsg("cannot alter type of a column used in a policy definition"),
14185 errdetail("%s depends on column \"%s\"",
14186 getObjectDescription(&foundObject
, false),
14190 case AttrDefaultRelationId
:
14192 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
14194 if (col
.objectId
== RelationGetRelid(rel
) &&
14195 col
.objectSubId
== attnum
)
14198 * Ignore the column's own default expression. The
14199 * caller deals with it.
14205 * This must be a reference from the expression of a
14206 * generated column elsewhere in the same table.
14207 * Changing the type/generated expression of a column
14208 * that is used by a generated column is not allowed
14209 * by SQL standard, so just punt for now. It might be
14210 * doable with some thinking and effort.
14212 if (subtype
== AT_AlterColumnType
)
14214 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14215 errmsg("cannot alter type of a column used by a generated column"),
14216 errdetail("Column \"%s\" is used by generated column \"%s\".",
14218 get_attname(col
.objectId
,
14225 case StatisticExtRelationId
:
14228 * Give the extended-stats machinery a chance to fix anything
14229 * that this column type change would break.
14231 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
14234 case PublicationRelRelationId
:
14237 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14238 * clause. Same issues as above. FIXME someday.
14240 if (subtype
== AT_AlterColumnType
)
14242 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14243 errmsg("cannot alter type of a column used by a publication WHERE clause"),
14244 errdetail("%s depends on column \"%s\"",
14245 getObjectDescription(&foundObject
, false),
14252 * We don't expect any other sorts of objects to depend on a
14255 elog(ERROR
, "unexpected object depending on column: %s",
14256 getObjectDescription(&foundObject
, false));
14261 systable_endscan(scan
);
14262 table_close(depRel
, NoLock
);
14266 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14267 * needs to be reset.
14270 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14272 if (!get_index_isreplident(indoid
))
14275 if (tab
->replicaIdentityIndex
)
14276 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
14278 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
14282 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14285 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14287 if (!get_index_isclustered(indoid
))
14290 if (tab
->clusterOnIndex
)
14291 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
14293 tab
->clusterOnIndex
= get_rel_name(indoid
);
14297 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14298 * to be rebuilt (which we might already know).
14301 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
14304 * This de-duplication check is critical for two independent reasons: we
14305 * mustn't try to recreate the same constraint twice, and if a constraint
14306 * depends on more than one column whose type is to be altered, we must
14307 * capture its definition string before applying any of the column type
14308 * changes. ruleutils.c will get confused if we ask again later.
14310 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
14312 /* OK, capture the constraint's existing definition string */
14313 char *defstring
= pg_get_constraintdef_command(conoid
);
14316 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
14318 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
14322 * For the index of a constraint, if any, remember if it is used for
14323 * the table's replica identity or if it is a clustered index, so that
14324 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14325 * those properties.
14327 indoid
= get_constraint_index(conoid
);
14328 if (OidIsValid(indoid
))
14330 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14331 RememberClusterOnForRebuilding(indoid
, tab
);
14337 * Subroutine for ATExecAlterColumnType: remember that an index needs
14338 * to be rebuilt (which we might already know).
14341 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14344 * This de-duplication check is critical for two independent reasons: we
14345 * mustn't try to recreate the same index twice, and if an index depends
14346 * on more than one column whose type is to be altered, we must capture
14347 * its definition string before applying any of the column type changes.
14348 * ruleutils.c will get confused if we ask again later.
14350 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
14353 * Before adding it as an index-to-rebuild, we'd better see if it
14354 * belongs to a constraint, and if so rebuild the constraint instead.
14355 * Typically this check fails, because constraint indexes normally
14356 * have only dependencies on their constraint. But it's possible for
14357 * such an index to also have direct dependencies on table columns,
14358 * for example with a partial exclusion constraint.
14360 Oid conoid
= get_index_constraint(indoid
);
14362 if (OidIsValid(conoid
))
14364 RememberConstraintForRebuilding(conoid
, tab
);
14368 /* OK, capture the index's existing definition string */
14369 char *defstring
= pg_get_indexdef_string(indoid
);
14371 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
14373 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
14377 * Remember if this index is used for the table's replica identity
14378 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14379 * can queue up commands necessary to restore those properties.
14381 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14382 RememberClusterOnForRebuilding(indoid
, tab
);
14388 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14389 * needs to be rebuilt (which we might already know).
14392 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
14395 * This de-duplication check is critical for two independent reasons: we
14396 * mustn't try to recreate the same statistics object twice, and if the
14397 * statistics object depends on more than one column whose type is to be
14398 * altered, we must capture its definition string before applying any of
14399 * the type changes. ruleutils.c will get confused if we ask again later.
14401 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
14403 /* OK, capture the statistics object's existing definition string */
14404 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
14406 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
14408 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
14414 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14415 * operations for a particular relation. We have to drop and recreate all the
14416 * indexes and constraints that depend on the altered columns. We do the
14417 * actual dropping here, but re-creation is managed by adding work queue
14418 * entries to do those steps later.
14421 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
14424 ObjectAddresses
*objects
;
14425 ListCell
*def_item
;
14426 ListCell
*oid_item
;
14429 * Collect all the constraints and indexes to drop so we can process them
14430 * in a single call. That way we don't have to worry about dependencies
14433 objects
= new_object_addresses();
14436 * Re-parse the index and constraint definitions, and attach them to the
14437 * appropriate work queue entries. We do this before dropping because in
14438 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14439 * lock on the table the constraint is attached to, and we need to get
14440 * that before reparsing/dropping.
14442 * We can't rely on the output of deparsing to tell us which relation to
14443 * operate on, because concurrent activity might have made the name
14444 * resolve differently. Instead, we've got to use the OID of the
14445 * constraint or index we're processing to figure out which relation to
14448 forboth(oid_item
, tab
->changedConstraintOids
,
14449 def_item
, tab
->changedConstraintDefs
)
14451 Oid oldId
= lfirst_oid(oid_item
);
14453 Form_pg_constraint con
;
14459 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14460 if (!HeapTupleIsValid(tup
)) /* should not happen */
14461 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14462 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
14463 if (OidIsValid(con
->conrelid
))
14464 relid
= con
->conrelid
;
14467 /* must be a domain constraint */
14468 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
14469 if (!OidIsValid(relid
))
14470 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
14472 confrelid
= con
->confrelid
;
14473 contype
= con
->contype
;
14474 conislocal
= con
->conislocal
;
14475 ReleaseSysCache(tup
);
14477 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
14478 add_exact_object_address(&obj
, objects
);
14481 * If the constraint is inherited (only), we don't want to inject a
14482 * new definition here; it'll get recreated when
14483 * ATAddCheckNNConstraint recurses from adding the parent table's
14484 * constraint. But we had to carry the info this far so that we can
14485 * drop the constraint below.
14491 * When rebuilding an FK constraint that references the table we're
14492 * modifying, we might not yet have any lock on the FK's table, so get
14493 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14494 * step, so there's no value in asking for anything weaker.
14496 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
14497 LockRelationOid(relid
, AccessExclusiveLock
);
14499 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
14500 (char *) lfirst(def_item
),
14501 wqueue
, lockmode
, tab
->rewrite
);
14503 forboth(oid_item
, tab
->changedIndexOids
,
14504 def_item
, tab
->changedIndexDefs
)
14506 Oid oldId
= lfirst_oid(oid_item
);
14509 relid
= IndexGetRelation(oldId
, false);
14510 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14511 (char *) lfirst(def_item
),
14512 wqueue
, lockmode
, tab
->rewrite
);
14514 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
14515 add_exact_object_address(&obj
, objects
);
14518 /* add dependencies for new statistics */
14519 forboth(oid_item
, tab
->changedStatisticsOids
,
14520 def_item
, tab
->changedStatisticsDefs
)
14522 Oid oldId
= lfirst_oid(oid_item
);
14525 relid
= StatisticsGetRelation(oldId
, false);
14526 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14527 (char *) lfirst(def_item
),
14528 wqueue
, lockmode
, tab
->rewrite
);
14530 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
14531 add_exact_object_address(&obj
, objects
);
14535 * Queue up command to restore replica identity index marking
14537 if (tab
->replicaIdentityIndex
)
14539 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14540 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
14542 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
14543 subcmd
->name
= tab
->replicaIdentityIndex
;
14544 cmd
->subtype
= AT_ReplicaIdentity
;
14545 cmd
->def
= (Node
*) subcmd
;
14547 /* do it after indexes and constraints */
14548 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14549 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14553 * Queue up command to restore marking of index used for cluster.
14555 if (tab
->clusterOnIndex
)
14557 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14559 cmd
->subtype
= AT_ClusterOn
;
14560 cmd
->name
= tab
->clusterOnIndex
;
14562 /* do it after indexes and constraints */
14563 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14564 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14568 * It should be okay to use DROP_RESTRICT here, since nothing else should
14569 * be depending on these objects.
14571 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
14573 free_object_addresses(objects
);
14576 * The objects will get recreated during subsequent passes over the work
14582 * Parse the previously-saved definition string for a constraint, index or
14583 * statistics object against the newly-established column data type(s), and
14584 * queue up the resulting command parsetrees for execution.
14586 * This might fail if, for example, you have a WHERE clause that uses an
14587 * operator that's not available for the new column type.
14590 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
14591 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
14593 List
*raw_parsetree_list
;
14594 List
*querytree_list
;
14595 ListCell
*list_item
;
14599 * We expect that we will get only ALTER TABLE and CREATE INDEX
14600 * statements. Hence, there is no need to pass them through
14601 * parse_analyze_*() or the rewriter, but instead we need to pass them
14602 * through parse_utilcmd.c to make them ready for execution.
14604 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
14605 querytree_list
= NIL
;
14606 foreach(list_item
, raw_parsetree_list
)
14608 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
14609 Node
*stmt
= rs
->stmt
;
14611 if (IsA(stmt
, IndexStmt
))
14612 querytree_list
= lappend(querytree_list
,
14613 transformIndexStmt(oldRelId
,
14614 (IndexStmt
*) stmt
,
14616 else if (IsA(stmt
, AlterTableStmt
))
14621 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
14622 (AlterTableStmt
*) stmt
,
14626 querytree_list
= list_concat(querytree_list
, beforeStmts
);
14627 querytree_list
= lappend(querytree_list
, stmt
);
14628 querytree_list
= list_concat(querytree_list
, afterStmts
);
14630 else if (IsA(stmt
, CreateStatsStmt
))
14631 querytree_list
= lappend(querytree_list
,
14632 transformStatsStmt(oldRelId
,
14633 (CreateStatsStmt
*) stmt
,
14636 querytree_list
= lappend(querytree_list
, stmt
);
14639 /* Caller should already have acquired whatever lock we need. */
14640 rel
= relation_open(oldRelId
, NoLock
);
14643 * Attach each generated command to the proper place in the work queue.
14644 * Note this could result in creation of entirely new work-queue entries.
14646 * Also note that we have to tweak the command subtypes, because it turns
14647 * out that re-creation of indexes and constraints has to act a bit
14648 * differently from initial creation.
14650 foreach(list_item
, querytree_list
)
14652 Node
*stm
= (Node
*) lfirst(list_item
);
14653 AlteredTableInfo
*tab
;
14655 tab
= ATGetQueueEntry(wqueue
, rel
);
14657 if (IsA(stm
, IndexStmt
))
14659 IndexStmt
*stmt
= (IndexStmt
*) stm
;
14660 AlterTableCmd
*newcmd
;
14663 TryReuseIndex(oldId
, stmt
);
14664 stmt
->reset_default_tblspc
= true;
14665 /* keep the index's comment */
14666 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
14668 newcmd
= makeNode(AlterTableCmd
);
14669 newcmd
->subtype
= AT_ReAddIndex
;
14670 newcmd
->def
= (Node
*) stmt
;
14671 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14672 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
14674 else if (IsA(stm
, AlterTableStmt
))
14676 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
14679 foreach(lcmd
, stmt
->cmds
)
14681 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
14683 if (cmd
->subtype
== AT_AddIndex
)
14685 IndexStmt
*indstmt
;
14688 indstmt
= castNode(IndexStmt
, cmd
->def
);
14689 indoid
= get_constraint_index(oldId
);
14692 TryReuseIndex(indoid
, indstmt
);
14693 /* keep any comment on the index */
14694 indstmt
->idxcomment
= GetComment(indoid
,
14695 RelationRelationId
, 0);
14696 indstmt
->reset_default_tblspc
= true;
14698 cmd
->subtype
= AT_ReAddIndex
;
14699 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14700 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
14702 /* recreate any comment on the constraint */
14703 RebuildConstraintComment(tab
,
14710 else if (cmd
->subtype
== AT_AddConstraint
)
14712 Constraint
*con
= castNode(Constraint
, cmd
->def
);
14714 con
->old_pktable_oid
= refRelId
;
14715 /* rewriting neither side of a FK */
14716 if (con
->contype
== CONSTR_FOREIGN
&&
14717 !rewrite
&& tab
->rewrite
== 0)
14718 TryReuseForeignKey(oldId
, con
);
14719 con
->reset_default_tblspc
= true;
14720 cmd
->subtype
= AT_ReAddConstraint
;
14721 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14722 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14724 /* recreate any comment on the constraint */
14725 RebuildConstraintComment(tab
,
14726 AT_PASS_OLD_CONSTR
,
14732 else if (cmd
->subtype
== AT_SetAttNotNull
)
14735 * We see this subtype when a primary key is created
14736 * internally, for example when it is replaced with a new
14737 * constraint (say because one of the columns changes
14738 * type); in this case we need to reinstate attnotnull,
14739 * because it was removed because of the drop of the old
14740 * PK. Schedule this subcommand to an upcoming AT pass.
14742 cmd
->subtype
= AT_SetAttNotNull
;
14743 tab
->subcmds
[AT_PASS_OLD_COL_ATTRS
] =
14744 lappend(tab
->subcmds
[AT_PASS_OLD_COL_ATTRS
], cmd
);
14747 elog(ERROR
, "unexpected statement subtype: %d",
14748 (int) cmd
->subtype
);
14751 else if (IsA(stm
, AlterDomainStmt
))
14753 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
14755 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
14757 Constraint
*con
= castNode(Constraint
, stmt
->def
);
14758 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14760 cmd
->subtype
= AT_ReAddDomainConstraint
;
14761 cmd
->def
= (Node
*) stmt
;
14762 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14763 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14765 /* recreate any comment on the constraint */
14766 RebuildConstraintComment(tab
,
14767 AT_PASS_OLD_CONSTR
,
14774 elog(ERROR
, "unexpected statement subtype: %d",
14775 (int) stmt
->subtype
);
14777 else if (IsA(stm
, CreateStatsStmt
))
14779 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
14780 AlterTableCmd
*newcmd
;
14782 /* keep the statistics object's comment */
14783 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
14785 newcmd
= makeNode(AlterTableCmd
);
14786 newcmd
->subtype
= AT_ReAddStatistics
;
14787 newcmd
->def
= (Node
*) stmt
;
14788 tab
->subcmds
[AT_PASS_MISC
] =
14789 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
14792 elog(ERROR
, "unexpected statement type: %d",
14793 (int) nodeTag(stm
));
14796 relation_close(rel
, NoLock
);
14800 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14801 * for a table or domain constraint that is being rebuilt.
14803 * objid is the OID of the constraint.
14804 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14805 * as a string list) for a domain constraint.
14806 * (We could dig that info, as well as the conname, out of the pg_constraint
14807 * entry; but callers already have them so might as well pass them.)
14810 RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
, Oid objid
,
14811 Relation rel
, List
*domname
,
14812 const char *conname
)
14816 AlterTableCmd
*newcmd
;
14818 /* Look for comment for object wanted, and leave if none */
14819 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
14820 if (comment_str
== NULL
)
14823 /* Build CommentStmt node, copying all input data for safety */
14824 cmd
= makeNode(CommentStmt
);
14827 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
14828 cmd
->object
= (Node
*)
14829 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
14830 makeString(pstrdup(RelationGetRelationName(rel
))),
14831 makeString(pstrdup(conname
)));
14835 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
14836 cmd
->object
= (Node
*)
14837 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
14838 makeString(pstrdup(conname
)));
14840 cmd
->comment
= comment_str
;
14842 /* Append it to list of commands */
14843 newcmd
= makeNode(AlterTableCmd
);
14844 newcmd
->subtype
= AT_ReAddComment
;
14845 newcmd
->def
= (Node
*) cmd
;
14846 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
14850 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14851 * for the real analysis, then mutates the IndexStmt based on that verdict.
14854 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
14856 if (CheckIndexCompatible(oldId
,
14857 stmt
->accessMethod
,
14859 stmt
->excludeOpNames
,
14860 stmt
->iswithoutoverlaps
))
14862 Relation irel
= index_open(oldId
, NoLock
);
14864 /* If it's a partitioned index, there is no storage to share. */
14865 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
14867 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
14868 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
14869 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
14871 index_close(irel
, NoLock
);
14876 * Subroutine for ATPostAlterTypeParse().
14878 * Stash the old P-F equality operator into the Constraint node, for possible
14879 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14880 * this constraint can be skipped.
14883 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
14892 Assert(con
->contype
== CONSTR_FOREIGN
);
14893 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
14895 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14896 if (!HeapTupleIsValid(tup
)) /* should not happen */
14897 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14899 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
14900 Anum_pg_constraint_conpfeqop
);
14901 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
14902 numkeys
= ARR_DIMS(arr
)[0];
14903 /* test follows the one in ri_FetchConstraintInfo() */
14904 if (ARR_NDIM(arr
) != 1 ||
14905 ARR_HASNULL(arr
) ||
14906 ARR_ELEMTYPE(arr
) != OIDOID
)
14907 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
14908 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
14910 /* stash a List of the operator Oids in our Constraint node */
14911 for (i
= 0; i
< numkeys
; i
++)
14912 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
14914 ReleaseSysCache(tup
);
14918 * ALTER COLUMN .. OPTIONS ( ... )
14920 * Returns the address of the modified column
14922 static ObjectAddress
14923 ATExecAlterColumnGenericOptions(Relation rel
,
14924 const char *colName
,
14930 ForeignServer
*server
;
14931 ForeignDataWrapper
*fdw
;
14933 HeapTuple newtuple
;
14935 Datum repl_val
[Natts_pg_attribute
];
14936 bool repl_null
[Natts_pg_attribute
];
14937 bool repl_repl
[Natts_pg_attribute
];
14939 Form_pg_foreign_table fttableform
;
14940 Form_pg_attribute atttableform
;
14942 ObjectAddress address
;
14944 if (options
== NIL
)
14945 return InvalidObjectAddress
;
14947 /* First, determine FDW validator associated to the foreign table. */
14948 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
14949 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
14950 if (!HeapTupleIsValid(tuple
))
14952 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14953 errmsg("foreign table \"%s\" does not exist",
14954 RelationGetRelationName(rel
))));
14955 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
14956 server
= GetForeignServer(fttableform
->ftserver
);
14957 fdw
= GetForeignDataWrapper(server
->fdwid
);
14959 table_close(ftrel
, AccessShareLock
);
14960 ReleaseSysCache(tuple
);
14962 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
14963 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
14964 if (!HeapTupleIsValid(tuple
))
14966 (errcode(ERRCODE_UNDEFINED_COLUMN
),
14967 errmsg("column \"%s\" of relation \"%s\" does not exist",
14968 colName
, RelationGetRelationName(rel
))));
14970 /* Prevent them from altering a system attribute */
14971 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
14972 attnum
= atttableform
->attnum
;
14975 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14976 errmsg("cannot alter system column \"%s\"", colName
)));
14979 /* Initialize buffers for new tuple values */
14980 memset(repl_val
, 0, sizeof(repl_val
));
14981 memset(repl_null
, false, sizeof(repl_null
));
14982 memset(repl_repl
, false, sizeof(repl_repl
));
14984 /* Extract the current options */
14985 datum
= SysCacheGetAttr(ATTNAME
,
14987 Anum_pg_attribute_attfdwoptions
,
14990 datum
= PointerGetDatum(NULL
);
14992 /* Transform the options */
14993 datum
= transformGenericOptions(AttributeRelationId
,
14996 fdw
->fdwvalidator
);
14998 if (PointerIsValid(DatumGetPointer(datum
)))
14999 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
15001 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
15003 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
15005 /* Everything looks good - update the tuple */
15007 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
15008 repl_val
, repl_null
, repl_repl
);
15010 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
15012 InvokeObjectPostAlterHook(RelationRelationId
,
15013 RelationGetRelid(rel
),
15014 atttableform
->attnum
);
15015 ObjectAddressSubSet(address
, RelationRelationId
,
15016 RelationGetRelid(rel
), attnum
);
15018 ReleaseSysCache(tuple
);
15020 table_close(attrel
, RowExclusiveLock
);
15022 heap_freetuple(newtuple
);
15028 * ALTER TABLE OWNER
15030 * recursing is true if we are recursing from a table to its indexes,
15031 * sequences, or toast table. We don't allow the ownership of those things to
15032 * be changed separately from the parent table. Also, we can skip permission
15033 * checks (this is necessary not just an optimization, else we'd fail to
15034 * handle toast tables properly).
15036 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15037 * free-standing composite type.
15040 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
15042 Relation target_rel
;
15043 Relation class_rel
;
15045 Form_pg_class tuple_class
;
15048 * Get exclusive lock till end of transaction on the target table. Use
15049 * relation_open so that we can work on indexes and sequences.
15051 target_rel
= relation_open(relationOid
, lockmode
);
15053 /* Get its pg_class tuple, too */
15054 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
15056 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
15057 if (!HeapTupleIsValid(tuple
))
15058 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
15059 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
15061 /* Can we change the ownership of this tuple? */
15062 switch (tuple_class
->relkind
)
15064 case RELKIND_RELATION
:
15066 case RELKIND_MATVIEW
:
15067 case RELKIND_FOREIGN_TABLE
:
15068 case RELKIND_PARTITIONED_TABLE
:
15069 /* ok to change owner */
15071 case RELKIND_INDEX
:
15075 * Because ALTER INDEX OWNER used to be allowed, and in fact
15076 * is generated by old versions of pg_dump, we give a warning
15077 * and do nothing rather than erroring out. Also, to avoid
15078 * unnecessary chatter while restoring those old dumps, say
15079 * nothing at all if the command would be a no-op anyway.
15081 if (tuple_class
->relowner
!= newOwnerId
)
15083 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15084 errmsg("cannot change owner of index \"%s\"",
15085 NameStr(tuple_class
->relname
)),
15086 errhint("Change the ownership of the index's table instead.")));
15087 /* quick hack to exit via the no-op path */
15088 newOwnerId
= tuple_class
->relowner
;
15091 case RELKIND_PARTITIONED_INDEX
:
15095 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15096 errmsg("cannot change owner of index \"%s\"",
15097 NameStr(tuple_class
->relname
)),
15098 errhint("Change the ownership of the index's table instead.")));
15100 case RELKIND_SEQUENCE
:
15102 tuple_class
->relowner
!= newOwnerId
)
15104 /* if it's an owned sequence, disallow changing it by itself */
15108 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
15109 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
15111 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15112 errmsg("cannot change owner of sequence \"%s\"",
15113 NameStr(tuple_class
->relname
)),
15114 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15115 NameStr(tuple_class
->relname
),
15116 get_rel_name(tableId
))));
15119 case RELKIND_COMPOSITE_TYPE
:
15123 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15124 errmsg("\"%s\" is a composite type",
15125 NameStr(tuple_class
->relname
)),
15126 /* translator: %s is an SQL ALTER command */
15127 errhint("Use %s instead.",
15130 case RELKIND_TOASTVALUE
:
15136 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15137 errmsg("cannot change owner of relation \"%s\"",
15138 NameStr(tuple_class
->relname
)),
15139 errdetail_relkind_not_supported(tuple_class
->relkind
)));
15143 * If the new owner is the same as the existing owner, consider the
15144 * command to have succeeded. This is for dump restoration purposes.
15146 if (tuple_class
->relowner
!= newOwnerId
)
15148 Datum repl_val
[Natts_pg_class
];
15149 bool repl_null
[Natts_pg_class
];
15150 bool repl_repl
[Natts_pg_class
];
15154 HeapTuple newtuple
;
15156 /* skip permission checks when recursing to index or toast table */
15159 /* Superusers can always do it */
15162 Oid namespaceOid
= tuple_class
->relnamespace
;
15163 AclResult aclresult
;
15165 /* Otherwise, must be owner of the existing object */
15166 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
15167 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
15168 RelationGetRelationName(target_rel
));
15170 /* Must be able to become new owner */
15171 check_can_set_role(GetUserId(), newOwnerId
);
15173 /* New owner must have CREATE privilege on namespace */
15174 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
15176 if (aclresult
!= ACLCHECK_OK
)
15177 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
15178 get_namespace_name(namespaceOid
));
15182 memset(repl_null
, false, sizeof(repl_null
));
15183 memset(repl_repl
, false, sizeof(repl_repl
));
15185 repl_repl
[Anum_pg_class_relowner
- 1] = true;
15186 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
15189 * Determine the modified ACL for the new owner. This is only
15190 * necessary when the ACL is non-null.
15192 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
15193 Anum_pg_class_relacl
,
15197 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15198 tuple_class
->relowner
, newOwnerId
);
15199 repl_repl
[Anum_pg_class_relacl
- 1] = true;
15200 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
15203 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
15205 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
15207 heap_freetuple(newtuple
);
15210 * We must similarly update any per-column ACLs to reflect the new
15211 * owner; for neatness reasons that's split out as a subroutine.
15213 change_owner_fix_column_acls(relationOid
,
15214 tuple_class
->relowner
,
15218 * Update owner dependency reference, if any. A composite type has
15219 * none, because it's tracked for the pg_type entry instead of here;
15220 * indexes and TOAST tables don't have their own entries either.
15222 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
15223 tuple_class
->relkind
!= RELKIND_INDEX
&&
15224 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
15225 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
15226 changeDependencyOnOwner(RelationRelationId
, relationOid
,
15230 * Also change the ownership of the table's row type, if it has one
15232 if (OidIsValid(tuple_class
->reltype
))
15233 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
15236 * If we are operating on a table or materialized view, also change
15237 * the ownership of any indexes and sequences that belong to the
15238 * relation, as well as its toast table (if it has one).
15240 if (tuple_class
->relkind
== RELKIND_RELATION
||
15241 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
15242 tuple_class
->relkind
== RELKIND_MATVIEW
||
15243 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
15245 List
*index_oid_list
;
15248 /* Find all the indexes belonging to this relation */
15249 index_oid_list
= RelationGetIndexList(target_rel
);
15251 /* For each index, recursively change its ownership */
15252 foreach(i
, index_oid_list
)
15253 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
15255 list_free(index_oid_list
);
15258 /* If it has a toast table, recurse to change its ownership */
15259 if (tuple_class
->reltoastrelid
!= InvalidOid
)
15260 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
15263 /* If it has dependent sequences, recurse to change them too */
15264 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
15267 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
15269 ReleaseSysCache(tuple
);
15270 table_close(class_rel
, RowExclusiveLock
);
15271 relation_close(target_rel
, NoLock
);
15275 * change_owner_fix_column_acls
15277 * Helper function for ATExecChangeOwner. Scan the columns of the table
15278 * and fix any non-null column ACLs to reflect the new owner.
15281 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
15283 Relation attRelation
;
15285 ScanKeyData key
[1];
15286 HeapTuple attributeTuple
;
15288 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
15289 ScanKeyInit(&key
[0],
15290 Anum_pg_attribute_attrelid
,
15291 BTEqualStrategyNumber
, F_OIDEQ
,
15292 ObjectIdGetDatum(relationOid
));
15293 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
15294 true, NULL
, 1, key
);
15295 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
15297 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
15298 Datum repl_val
[Natts_pg_attribute
];
15299 bool repl_null
[Natts_pg_attribute
];
15300 bool repl_repl
[Natts_pg_attribute
];
15304 HeapTuple newtuple
;
15306 /* Ignore dropped columns */
15307 if (att
->attisdropped
)
15310 aclDatum
= heap_getattr(attributeTuple
,
15311 Anum_pg_attribute_attacl
,
15312 RelationGetDescr(attRelation
),
15314 /* Null ACLs do not require changes */
15318 memset(repl_null
, false, sizeof(repl_null
));
15319 memset(repl_repl
, false, sizeof(repl_repl
));
15321 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15322 oldOwnerId
, newOwnerId
);
15323 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
15324 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
15326 newtuple
= heap_modify_tuple(attributeTuple
,
15327 RelationGetDescr(attRelation
),
15328 repl_val
, repl_null
, repl_repl
);
15330 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
15332 heap_freetuple(newtuple
);
15334 systable_endscan(scan
);
15335 table_close(attRelation
, RowExclusiveLock
);
15339 * change_owner_recurse_to_sequences
15341 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15342 * for sequences that are dependent on serial columns, and changes their
15346 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
15350 ScanKeyData key
[2];
15354 * SERIAL sequences are those having an auto dependency on one of the
15355 * table's columns (we don't care *which* column, exactly).
15357 depRel
= table_open(DependRelationId
, AccessShareLock
);
15359 ScanKeyInit(&key
[0],
15360 Anum_pg_depend_refclassid
,
15361 BTEqualStrategyNumber
, F_OIDEQ
,
15362 ObjectIdGetDatum(RelationRelationId
));
15363 ScanKeyInit(&key
[1],
15364 Anum_pg_depend_refobjid
,
15365 BTEqualStrategyNumber
, F_OIDEQ
,
15366 ObjectIdGetDatum(relationOid
));
15367 /* we leave refobjsubid unspecified */
15369 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
15372 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
15374 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
15377 /* skip dependencies other than auto dependencies on columns */
15378 if (depForm
->refobjsubid
== 0 ||
15379 depForm
->classid
!= RelationRelationId
||
15380 depForm
->objsubid
!= 0 ||
15381 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
15384 /* Use relation_open just in case it's an index */
15385 seqRel
= relation_open(depForm
->objid
, lockmode
);
15387 /* skip non-sequence relations */
15388 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
15390 /* No need to keep the lock */
15391 relation_close(seqRel
, lockmode
);
15395 /* We don't need to close the sequence while we alter it. */
15396 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
15398 /* Now we can close it. Keep the lock till end of transaction. */
15399 relation_close(seqRel
, NoLock
);
15402 systable_endscan(scan
);
15404 relation_close(depRel
, AccessShareLock
);
15408 * ALTER TABLE CLUSTER ON
15410 * The only thing we have to do is to change the indisclustered bits.
15412 * Return the address of the new clustering index.
15414 static ObjectAddress
15415 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
15418 ObjectAddress address
;
15420 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
15422 if (!OidIsValid(indexOid
))
15424 (errcode(ERRCODE_UNDEFINED_OBJECT
),
15425 errmsg("index \"%s\" for table \"%s\" does not exist",
15426 indexName
, RelationGetRelationName(rel
))));
15428 /* Check index is valid to cluster on */
15429 check_index_is_clusterable(rel
, indexOid
, lockmode
);
15431 /* And do the work */
15432 mark_index_clustered(rel
, indexOid
, false);
15434 ObjectAddressSet(address
,
15435 RelationRelationId
, indexOid
);
15441 * ALTER TABLE SET WITHOUT CLUSTER
15443 * We have to find any indexes on the table that have indisclustered bit
15444 * set and turn it off.
15447 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
15449 mark_index_clustered(rel
, InvalidOid
, false);
15453 * Preparation phase for SET ACCESS METHOD
15455 * Check that the access method exists and determine whether a change is
15459 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
15464 * Look up the access method name and check that it differs from the
15465 * table's current AM. If DEFAULT was specified for a partitioned table
15466 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15468 if (amname
!= NULL
)
15469 amoid
= get_table_am_oid(amname
, false);
15470 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15471 amoid
= InvalidOid
;
15473 amoid
= get_table_am_oid(default_table_access_method
, false);
15475 /* if it's a match, phase 3 doesn't need to do anything */
15476 if (rel
->rd_rel
->relam
== amoid
)
15479 /* Save info for Phase 3 to do the real work */
15480 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
15481 tab
->newAccessMethod
= amoid
;
15482 tab
->chgAccessMethod
= true;
15486 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15487 * storage that have an interest in preserving AM.
15489 * Since these have no storage, setting the access method is a catalog only
15493 ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
)
15496 Oid oldAccessMethodId
;
15498 Form_pg_class rd_rel
;
15499 Oid reloid
= RelationGetRelid(rel
);
15502 * Shouldn't be called on relations having storage; these are processed in
15505 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15507 /* Get a modifiable copy of the relation's pg_class row. */
15508 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
15510 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
15511 if (!HeapTupleIsValid(tuple
))
15512 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
15513 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
15515 /* Update the pg_class row. */
15516 oldAccessMethodId
= rd_rel
->relam
;
15517 rd_rel
->relam
= newAccessMethodId
;
15519 /* Leave if no update required */
15520 if (rd_rel
->relam
== oldAccessMethodId
)
15522 heap_freetuple(tuple
);
15523 table_close(pg_class
, RowExclusiveLock
);
15527 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
15530 * Update the dependency on the new access method. No dependency is added
15531 * if the new access method is InvalidOid (default case). Be very careful
15532 * that this has to compare the previous value stored in pg_class with the
15535 if (!OidIsValid(oldAccessMethodId
) && OidIsValid(rd_rel
->relam
))
15537 ObjectAddress relobj
,
15541 * New access method is defined and there was no dependency
15542 * previously, so record a new one.
15544 ObjectAddressSet(relobj
, RelationRelationId
, reloid
);
15545 ObjectAddressSet(referenced
, AccessMethodRelationId
, rd_rel
->relam
);
15546 recordDependencyOn(&relobj
, &referenced
, DEPENDENCY_NORMAL
);
15548 else if (OidIsValid(oldAccessMethodId
) &&
15549 !OidIsValid(rd_rel
->relam
))
15552 * There was an access method defined, and no new one, so just remove
15553 * the existing dependency.
15555 deleteDependencyRecordsForClass(RelationRelationId
, reloid
,
15556 AccessMethodRelationId
,
15557 DEPENDENCY_NORMAL
);
15561 Assert(OidIsValid(oldAccessMethodId
) &&
15562 OidIsValid(rd_rel
->relam
));
15564 /* Both are valid, so update the dependency */
15565 changeDependencyFor(RelationRelationId
, reloid
,
15566 AccessMethodRelationId
,
15567 oldAccessMethodId
, rd_rel
->relam
);
15570 /* make the relam and dependency changes visible */
15571 CommandCounterIncrement();
15573 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15575 heap_freetuple(tuple
);
15576 table_close(pg_class
, RowExclusiveLock
);
15580 * ALTER TABLE SET TABLESPACE
15583 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
15587 /* Check that the tablespace exists */
15588 tablespaceId
= get_tablespace_oid(tablespacename
, false);
15590 /* Check permissions except when moving to database's default */
15591 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
15593 AclResult aclresult
;
15595 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
15596 if (aclresult
!= ACLCHECK_OK
)
15597 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
15600 /* Save info for Phase 3 to do the real work */
15601 if (OidIsValid(tab
->newTableSpace
))
15603 (errcode(ERRCODE_SYNTAX_ERROR
),
15604 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15606 tab
->newTableSpace
= tablespaceId
;
15610 * Set, reset, or replace reloptions.
15613 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
15619 HeapTuple newtuple
;
15623 Datum repl_val
[Natts_pg_class
];
15624 bool repl_null
[Natts_pg_class
];
15625 bool repl_repl
[Natts_pg_class
];
15626 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
15628 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
15629 return; /* nothing to do */
15631 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
15633 /* Fetch heap tuple */
15634 relid
= RelationGetRelid(rel
);
15635 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
15636 if (!HeapTupleIsValid(tuple
))
15637 elog(ERROR
, "cache lookup failed for relation %u", relid
);
15639 if (operation
== AT_ReplaceRelOptions
)
15642 * If we're supposed to replace the reloptions list, we just pretend
15643 * there were none before.
15650 /* Get the old reloptions */
15651 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15655 /* Generate new proposed reloptions (text array) */
15656 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15657 defList
, NULL
, validnsps
, false,
15658 operation
== AT_ResetRelOptions
);
15661 switch (rel
->rd_rel
->relkind
)
15663 case RELKIND_RELATION
:
15664 case RELKIND_TOASTVALUE
:
15665 case RELKIND_MATVIEW
:
15666 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
15668 case RELKIND_PARTITIONED_TABLE
:
15669 (void) partitioned_table_reloptions(newOptions
, true);
15672 (void) view_reloptions(newOptions
, true);
15674 case RELKIND_INDEX
:
15675 case RELKIND_PARTITIONED_INDEX
:
15676 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
15680 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15681 errmsg("cannot set options for relation \"%s\"",
15682 RelationGetRelationName(rel
)),
15683 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
15687 /* Special-case validation of view options */
15688 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
15690 Query
*view_query
= get_view_query(rel
);
15691 List
*view_options
= untransformRelOptions(newOptions
);
15693 bool check_option
= false;
15695 foreach(cell
, view_options
)
15697 DefElem
*defel
= (DefElem
*) lfirst(cell
);
15699 if (strcmp(defel
->defname
, "check_option") == 0)
15700 check_option
= true;
15704 * If the check option is specified, look to see if the view is
15705 * actually auto-updatable or not.
15709 const char *view_updatable_error
=
15710 view_query_is_auto_updatable(view_query
, true);
15712 if (view_updatable_error
)
15714 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15715 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15716 errhint("%s", _(view_updatable_error
))));
15721 * All we need do here is update the pg_class row; the new options will be
15722 * propagated into relcaches during post-commit cache inval.
15724 memset(repl_val
, 0, sizeof(repl_val
));
15725 memset(repl_null
, false, sizeof(repl_null
));
15726 memset(repl_repl
, false, sizeof(repl_repl
));
15728 if (newOptions
!= (Datum
) 0)
15729 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15731 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15733 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15735 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15736 repl_val
, repl_null
, repl_repl
);
15738 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15740 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15742 heap_freetuple(newtuple
);
15744 ReleaseSysCache(tuple
);
15746 /* repeat the whole exercise for the toast table, if there's one */
15747 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
15750 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
15752 toastrel
= table_open(toastid
, lockmode
);
15754 /* Fetch heap tuple */
15755 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
15756 if (!HeapTupleIsValid(tuple
))
15757 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
15759 if (operation
== AT_ReplaceRelOptions
)
15762 * If we're supposed to replace the reloptions list, we just
15763 * pretend there were none before.
15770 /* Get the old reloptions */
15771 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15775 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15776 defList
, "toast", validnsps
, false,
15777 operation
== AT_ResetRelOptions
);
15779 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
15781 memset(repl_val
, 0, sizeof(repl_val
));
15782 memset(repl_null
, false, sizeof(repl_null
));
15783 memset(repl_repl
, false, sizeof(repl_repl
));
15785 if (newOptions
!= (Datum
) 0)
15786 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15788 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15790 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15792 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15793 repl_val
, repl_null
, repl_repl
);
15795 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15797 InvokeObjectPostAlterHookArg(RelationRelationId
,
15798 RelationGetRelid(toastrel
), 0,
15801 heap_freetuple(newtuple
);
15803 ReleaseSysCache(tuple
);
15805 table_close(toastrel
, NoLock
);
15808 table_close(pgclass
, RowExclusiveLock
);
15812 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15813 * rewriting to be done, so we just want to copy the data as fast as possible.
15816 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
15820 RelFileNumber newrelfilenumber
;
15821 RelFileLocator newrlocator
;
15822 List
*reltoastidxids
= NIL
;
15826 * Need lock here in case we are recursing to toast table or index
15828 rel
= relation_open(tableOid
, lockmode
);
15830 /* Check first if relation can be moved to new tablespace */
15831 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15833 InvokeObjectPostAlterHook(RelationRelationId
,
15834 RelationGetRelid(rel
), 0);
15835 relation_close(rel
, NoLock
);
15839 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
15840 /* Fetch the list of indexes on toast relation if necessary */
15841 if (OidIsValid(reltoastrelid
))
15843 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
15845 reltoastidxids
= RelationGetIndexList(toastRel
);
15846 relation_close(toastRel
, lockmode
);
15850 * Relfilenumbers are not unique in databases across tablespaces, so we
15851 * need to allocate a new one in the new tablespace.
15853 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
15854 rel
->rd_rel
->relpersistence
);
15856 /* Open old and new relation */
15857 newrlocator
= rel
->rd_locator
;
15858 newrlocator
.relNumber
= newrelfilenumber
;
15859 newrlocator
.spcOid
= newTableSpace
;
15861 /* hand off to AM to actually create new rel storage and copy the data */
15862 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
15864 index_copy_data(rel
, newrlocator
);
15868 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
15869 table_relation_copy_data(rel
, &newrlocator
);
15873 * Update the pg_class row.
15875 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15876 * executed on pg_class or its indexes (the above copy wouldn't contain
15877 * the updated pg_class entry), but that's forbidden with
15878 * CheckRelationTableSpaceMove().
15880 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
15882 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15884 RelationAssumeNewRelfilelocator(rel
);
15886 relation_close(rel
, NoLock
);
15888 /* Make sure the reltablespace change is visible */
15889 CommandCounterIncrement();
15891 /* Move associated toast relation and/or indexes, too */
15892 if (OidIsValid(reltoastrelid
))
15893 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
15894 foreach(lc
, reltoastidxids
)
15895 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
15898 list_free(reltoastidxids
);
15902 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15903 * storage that have an interest in preserving tablespace.
15905 * Since these have no storage the tablespace can be updated with a simple
15906 * metadata only operation to update the tablespace.
15909 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
15912 * Shouldn't be called on relations having storage; these are processed in
15915 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15917 /* check if relation can be moved to its new tablespace */
15918 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15920 InvokeObjectPostAlterHook(RelationRelationId
,
15921 RelationGetRelid(rel
),
15926 /* Update can be done, so change reltablespace */
15927 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
15929 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15931 /* Make sure the reltablespace change is visible */
15932 CommandCounterIncrement();
15936 * Alter Table ALL ... SET TABLESPACE
15938 * Allows a user to move all objects of some type in a given tablespace in the
15939 * current database to another tablespace. Objects can be chosen based on the
15940 * owner of the object also, to allow users to move only their objects.
15941 * The user must have CREATE rights on the new tablespace, as usual. The main
15942 * permissions handling is done by the lower-level table move function.
15944 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15945 * lock can't be acquired then we ereport(ERROR).
15948 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
15950 List
*relations
= NIL
;
15952 ScanKeyData key
[1];
15954 TableScanDesc scan
;
15956 Oid orig_tablespaceoid
;
15957 Oid new_tablespaceoid
;
15958 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
15960 /* Ensure we were not asked to move something we can't */
15961 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
15962 stmt
->objtype
!= OBJECT_MATVIEW
)
15964 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15965 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15967 /* Get the orig and new tablespace OIDs */
15968 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
15969 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
15971 /* Can't move shared relations in to or out of pg_global */
15972 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15973 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
15974 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
15976 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15977 errmsg("cannot move relations in to or out of pg_global tablespace")));
15980 * Must have CREATE rights on the new tablespace, unless it is the
15981 * database default tablespace (which all users implicitly have CREATE
15984 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
15986 AclResult aclresult
;
15988 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
15990 if (aclresult
!= ACLCHECK_OK
)
15991 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
15992 get_tablespace_name(new_tablespaceoid
));
15996 * Now that the checks are done, check if we should set either to
15997 * InvalidOid because it is our database's default tablespace.
15999 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
16000 orig_tablespaceoid
= InvalidOid
;
16002 if (new_tablespaceoid
== MyDatabaseTableSpace
)
16003 new_tablespaceoid
= InvalidOid
;
16006 if (orig_tablespaceoid
== new_tablespaceoid
)
16007 return new_tablespaceoid
;
16010 * Walk the list of objects in the tablespace and move them. This will
16011 * only find objects in our database, of course.
16013 ScanKeyInit(&key
[0],
16014 Anum_pg_class_reltablespace
,
16015 BTEqualStrategyNumber
, F_OIDEQ
,
16016 ObjectIdGetDatum(orig_tablespaceoid
));
16018 rel
= table_open(RelationRelationId
, AccessShareLock
);
16019 scan
= table_beginscan_catalog(rel
, 1, key
);
16020 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
16022 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
16023 Oid relOid
= relForm
->oid
;
16026 * Do not move objects in pg_catalog as part of this, if an admin
16027 * really wishes to do so, they can issue the individual ALTER
16028 * commands directly.
16030 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16031 * (TOAST will be moved with the main table).
16033 if (IsCatalogNamespace(relForm
->relnamespace
) ||
16034 relForm
->relisshared
||
16035 isAnyTempNamespace(relForm
->relnamespace
) ||
16036 IsToastNamespace(relForm
->relnamespace
))
16039 /* Only move the object type requested */
16040 if ((stmt
->objtype
== OBJECT_TABLE
&&
16041 relForm
->relkind
!= RELKIND_RELATION
&&
16042 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
16043 (stmt
->objtype
== OBJECT_INDEX
&&
16044 relForm
->relkind
!= RELKIND_INDEX
&&
16045 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
16046 (stmt
->objtype
== OBJECT_MATVIEW
&&
16047 relForm
->relkind
!= RELKIND_MATVIEW
))
16050 /* Check if we are only moving objects owned by certain roles */
16051 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
16055 * Handle permissions-checking here since we are locking the tables
16056 * and also to avoid doing a bunch of work only to fail part-way. Note
16057 * that permissions will also be checked by AlterTableInternal().
16059 * Caller must be considered an owner on the table to move it.
16061 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
16062 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
16063 NameStr(relForm
->relname
));
16065 if (stmt
->nowait
&&
16066 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
16068 (errcode(ERRCODE_OBJECT_IN_USE
),
16069 errmsg("aborting because lock on relation \"%s.%s\" is not available",
16070 get_namespace_name(relForm
->relnamespace
),
16071 NameStr(relForm
->relname
))));
16073 LockRelationOid(relOid
, AccessExclusiveLock
);
16075 /* Add to our list of objects to move */
16076 relations
= lappend_oid(relations
, relOid
);
16079 table_endscan(scan
);
16080 table_close(rel
, AccessShareLock
);
16082 if (relations
== NIL
)
16084 (errcode(ERRCODE_NO_DATA_FOUND
),
16085 errmsg("no matching relations in tablespace \"%s\" found",
16086 orig_tablespaceoid
== InvalidOid
? "(database default)" :
16087 get_tablespace_name(orig_tablespaceoid
))));
16089 /* Everything is locked, loop through and move all of the relations. */
16090 foreach(l
, relations
)
16093 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
16095 cmd
->subtype
= AT_SetTableSpace
;
16096 cmd
->name
= stmt
->new_tablespacename
;
16098 cmds
= lappend(cmds
, cmd
);
16100 EventTriggerAlterTableStart((Node
*) stmt
);
16101 /* OID is set by AlterTableInternal */
16102 AlterTableInternal(lfirst_oid(l
), cmds
, false);
16103 EventTriggerAlterTableEnd();
16106 return new_tablespaceoid
;
16110 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
16112 SMgrRelation dstrel
;
16115 * Since we copy the file directly without looking at the shared buffers,
16116 * we'd better first flush out any pages of the source relation that are
16117 * in shared buffers. We assume no new changes will be made while we are
16118 * holding exclusive lock on the rel.
16120 FlushRelationBuffers(rel
);
16123 * Create and copy all forks of the relation, and schedule unlinking of
16124 * old physical files.
16126 * NOTE: any conflict in relfilenumber value will be caught in
16127 * RelationCreateStorage().
16129 dstrel
= RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
16131 /* copy main fork */
16132 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
16133 rel
->rd_rel
->relpersistence
);
16135 /* copy those extra forks that exist */
16136 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
16137 forkNum
<= MAX_FORKNUM
; forkNum
++)
16139 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
16141 smgrcreate(dstrel
, forkNum
, false);
16144 * WAL log creation if the relation is persistent, or this is the
16145 * init fork of an unlogged relation.
16147 if (RelationIsPermanent(rel
) ||
16148 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
16149 forkNum
== INIT_FORKNUM
))
16150 log_smgrcreate(&newrlocator
, forkNum
);
16151 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
16152 rel
->rd_rel
->relpersistence
);
16156 /* drop old relation, and close new one */
16157 RelationDropStorage(rel
);
16162 * ALTER TABLE ENABLE/DISABLE TRIGGER
16164 * We just pass this off to trigger.c.
16167 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
16168 char fires_when
, bool skip_system
, bool recurse
,
16171 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
16172 fires_when
, skip_system
, recurse
,
16175 InvokeObjectPostAlterHook(RelationRelationId
,
16176 RelationGetRelid(rel
), 0);
16180 * ALTER TABLE ENABLE/DISABLE RULE
16182 * We just pass this off to rewriteDefine.c.
16185 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
16186 char fires_when
, LOCKMODE lockmode
)
16188 EnableDisableRule(rel
, rulename
, fires_when
);
16190 InvokeObjectPostAlterHook(RelationRelationId
,
16191 RelationGetRelid(rel
), 0);
16195 * ALTER TABLE INHERIT
16197 * Add a parent to the child's parents. This verifies that all the columns and
16198 * check constraints of the parent appear in the child and that they have the
16199 * same data types and expressions.
16202 ATPrepAddInherit(Relation child_rel
)
16204 if (child_rel
->rd_rel
->reloftype
)
16206 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16207 errmsg("cannot change inheritance of typed table")));
16209 if (child_rel
->rd_rel
->relispartition
)
16211 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16212 errmsg("cannot change inheritance of a partition")));
16214 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16216 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16217 errmsg("cannot change inheritance of partitioned table")));
16221 * Return the address of the new parent relation.
16223 static ObjectAddress
16224 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16226 Relation parent_rel
;
16228 ObjectAddress address
;
16229 const char *trigger_name
;
16232 * A self-exclusive lock is needed here. See the similar case in
16233 * MergeAttributes() for a full explanation.
16235 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
16238 * Must be owner of both parent and child -- child was checked by
16239 * ATSimplePermissions call in ATPrepCmd
16241 ATSimplePermissions(AT_AddInherit
, parent_rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
16243 /* Permanent rels cannot inherit from temporary ones */
16244 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16245 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
16247 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16248 errmsg("cannot inherit from temporary relation \"%s\"",
16249 RelationGetRelationName(parent_rel
))));
16251 /* If parent rel is temp, it must belong to this session */
16252 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16253 !parent_rel
->rd_islocaltemp
)
16255 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16256 errmsg("cannot inherit from temporary relation of another session")));
16258 /* Ditto for the child */
16259 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16260 !child_rel
->rd_islocaltemp
)
16262 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16263 errmsg("cannot inherit to temporary relation of another session")));
16265 /* Prevent partitioned tables from becoming inheritance parents */
16266 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16268 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16269 errmsg("cannot inherit from partitioned table \"%s\"",
16270 parent
->relname
)));
16272 /* Likewise for partitions */
16273 if (parent_rel
->rd_rel
->relispartition
)
16275 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16276 errmsg("cannot inherit from a partition")));
16279 * Prevent circularity by seeing if proposed parent inherits from child.
16280 * (In particular, this disallows making a rel inherit from itself.)
16282 * This is not completely bulletproof because of race conditions: in
16283 * multi-level inheritance trees, someone else could concurrently be
16284 * making another inheritance link that closes the loop but does not join
16285 * either of the rels we have locked. Preventing that seems to require
16286 * exclusive locks on the entire inheritance tree, which is a cure worse
16287 * than the disease. find_all_inheritors() will cope with circularity
16288 * anyway, so don't sweat it too much.
16290 * We use weakest lock we can on child's children, namely AccessShareLock.
16292 children
= find_all_inheritors(RelationGetRelid(child_rel
),
16293 AccessShareLock
, NULL
);
16295 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
16297 (errcode(ERRCODE_DUPLICATE_TABLE
),
16298 errmsg("circular inheritance not allowed"),
16299 errdetail("\"%s\" is already a child of \"%s\".",
16301 RelationGetRelationName(child_rel
))));
16304 * If child_rel has row-level triggers with transition tables, we
16305 * currently don't allow it to become an inheritance child. See also
16306 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16308 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
16309 if (trigger_name
!= NULL
)
16311 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16312 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16313 trigger_name
, RelationGetRelationName(child_rel
)),
16314 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16316 /* OK to create inheritance */
16317 CreateInheritance(child_rel
, parent_rel
, false);
16320 * If parent_rel has a primary key, then child_rel has not-null
16321 * constraints that make these columns as non nullable. Make those
16322 * constraints as inherited.
16324 ATInheritAdjustNotNulls(parent_rel
, child_rel
, 1);
16326 ObjectAddressSet(address
, RelationRelationId
,
16327 RelationGetRelid(parent_rel
));
16329 /* keep our lock on the parent relation until commit */
16330 table_close(parent_rel
, NoLock
);
16336 * CreateInheritance
16337 * Catalog manipulation portion of creating inheritance between a child
16338 * table and a parent table.
16340 * Common to ATExecAddInherit() and ATExecAttachPartition().
16343 CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16345 Relation catalogRelation
;
16348 HeapTuple inheritsTuple
;
16351 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16352 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16355 * Check for duplicates in the list of parents, and determine the highest
16356 * inhseqno already present; we'll use the next one for the new parent.
16357 * Also, if proposed child is a partition, it cannot already be
16360 * Note: we do not reject the case where the child already inherits from
16361 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16364 Anum_pg_inherits_inhrelid
,
16365 BTEqualStrategyNumber
, F_OIDEQ
,
16366 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16367 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
16368 true, NULL
, 1, &key
);
16370 /* inhseqno sequences start at 1 */
16372 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16374 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16376 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
16378 (errcode(ERRCODE_DUPLICATE_TABLE
),
16379 errmsg("relation \"%s\" would be inherited from more than once",
16380 RelationGetRelationName(parent_rel
))));
16382 if (inh
->inhseqno
> inhseqno
)
16383 inhseqno
= inh
->inhseqno
;
16385 systable_endscan(scan
);
16387 /* Match up the columns and bump attinhcount as needed */
16388 MergeAttributesIntoExisting(child_rel
, parent_rel
, ispartition
);
16390 /* Match up the constraints and bump coninhcount as needed */
16391 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
16394 * OK, it looks valid. Make the catalog entries that show inheritance.
16396 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
16397 RelationGetRelid(parent_rel
),
16400 parent_rel
->rd_rel
->relkind
==
16401 RELKIND_PARTITIONED_TABLE
);
16403 /* Now we're done with pg_inherits */
16404 table_close(catalogRelation
, RowExclusiveLock
);
16408 * Obtain the source-text form of the constraint expression for a check
16409 * constraint, given its pg_constraint tuple
16412 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
16414 Form_pg_constraint con
;
16419 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
16420 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
16422 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
16424 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
16425 ObjectIdGetDatum(con
->conrelid
));
16426 return TextDatumGetCString(expr
);
16430 * Determine whether two check constraints are functionally equivalent
16432 * The test we apply is to see whether they reverse-compile to the same
16433 * source string. This insulates us from issues like whether attributes
16434 * have the same physical column numbers in parent and child relations.
16437 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
16439 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
16440 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
16442 if (acon
->condeferrable
!= bcon
->condeferrable
||
16443 acon
->condeferred
!= bcon
->condeferred
||
16444 strcmp(decompile_conbin(a
, tupleDesc
),
16445 decompile_conbin(b
, tupleDesc
)) != 0)
16452 * Check columns in child table match up with columns in parent, and increment
16453 * their attinhcount.
16455 * Called by CreateInheritance
16457 * Currently all parent columns must be found in child. Missing columns are an
16458 * error. One day we might consider creating new columns like CREATE TABLE
16459 * does. However, that is widely unpopular --- in the common use case of
16460 * partitioned tables it's a foot-gun.
16462 * The data type must match exactly. If the parent column is NOT NULL then
16463 * the child must be as well. Defaults are not compared, however.
16466 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16469 TupleDesc parent_desc
;
16471 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
16472 parent_desc
= RelationGetDescr(parent_rel
);
16474 for (AttrNumber parent_attno
= 1; parent_attno
<= parent_desc
->natts
; parent_attno
++)
16476 Form_pg_attribute parent_att
= TupleDescAttr(parent_desc
, parent_attno
- 1);
16477 char *parent_attname
= NameStr(parent_att
->attname
);
16480 /* Ignore dropped columns in the parent. */
16481 if (parent_att
->attisdropped
)
16484 /* Find same column in child (matching on column name). */
16485 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
), parent_attname
);
16486 if (HeapTupleIsValid(tuple
))
16488 Form_pg_attribute child_att
= (Form_pg_attribute
) GETSTRUCT(tuple
);
16490 if (parent_att
->atttypid
!= child_att
->atttypid
||
16491 parent_att
->atttypmod
!= child_att
->atttypmod
)
16493 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16494 errmsg("child table \"%s\" has different type for column \"%s\"",
16495 RelationGetRelationName(child_rel
), parent_attname
)));
16497 if (parent_att
->attcollation
!= child_att
->attcollation
)
16499 (errcode(ERRCODE_COLLATION_MISMATCH
),
16500 errmsg("child table \"%s\" has different collation for column \"%s\"",
16501 RelationGetRelationName(child_rel
), parent_attname
)));
16504 * If the parent has a not-null constraint that's not NO INHERIT,
16505 * make sure the child has one too.
16507 * Other constraints are checked elsewhere.
16509 if (parent_att
->attnotnull
&& !child_att
->attnotnull
)
16513 contup
= findNotNullConstraintAttnum(RelationGetRelid(parent_rel
),
16514 parent_att
->attnum
);
16515 if (HeapTupleIsValid(contup
) &&
16516 !((Form_pg_constraint
) GETSTRUCT(contup
))->connoinherit
)
16518 errcode(ERRCODE_DATATYPE_MISMATCH
),
16519 errmsg("column \"%s\" in child table must be marked NOT NULL",
16524 * Child column must be generated if and only if parent column is.
16526 if (parent_att
->attgenerated
&& !child_att
->attgenerated
)
16528 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16529 errmsg("column \"%s\" in child table must be a generated column", parent_attname
)));
16530 if (child_att
->attgenerated
&& !parent_att
->attgenerated
)
16532 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16533 errmsg("column \"%s\" in child table must not be a generated column", parent_attname
)));
16536 * Regular inheritance children are independent enough not to
16537 * inherit identity columns. But partitions are integral part of
16538 * a partitioned table and inherit identity column.
16541 child_att
->attidentity
= parent_att
->attidentity
;
16544 * OK, bump the child column's inheritance count. (If we fail
16545 * later on, this change will just roll back.)
16547 child_att
->attinhcount
++;
16548 if (child_att
->attinhcount
< 0)
16550 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16551 errmsg("too many inheritance parents"));
16554 * In case of partitions, we must enforce that value of attislocal
16555 * is same in all partitions. (Note: there are only inherited
16556 * attributes in partitions)
16558 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16560 Assert(child_att
->attinhcount
== 1);
16561 child_att
->attislocal
= false;
16564 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
16565 heap_freetuple(tuple
);
16570 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16571 errmsg("child table is missing column \"%s\"", parent_attname
)));
16575 table_close(attrrel
, RowExclusiveLock
);
16579 * Check constraints in child table match up with constraints in parent,
16580 * and increment their coninhcount.
16582 * Constraints that are marked ONLY in the parent are ignored.
16584 * Called by CreateInheritance
16586 * Currently all constraints in parent must be present in the child. One day we
16587 * may consider adding new constraints like CREATE TABLE does.
16589 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16590 * constraints. As long as tables have more like 10 constraints it shouldn't be
16591 * a problem though. Even 100 constraints ought not be the end of the world.
16593 * XXX See MergeWithExistingConstraint too if you change this code.
16596 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
16598 Relation constraintrel
;
16599 SysScanDesc parent_scan
;
16600 ScanKeyData parent_key
;
16601 HeapTuple parent_tuple
;
16602 Oid parent_relid
= RelationGetRelid(parent_rel
);
16604 constraintrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16606 /* Outer loop scans through the parent's constraint definitions */
16607 ScanKeyInit(&parent_key
,
16608 Anum_pg_constraint_conrelid
,
16609 BTEqualStrategyNumber
, F_OIDEQ
,
16610 ObjectIdGetDatum(parent_relid
));
16611 parent_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16612 true, NULL
, 1, &parent_key
);
16614 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
16616 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
16617 SysScanDesc child_scan
;
16618 ScanKeyData child_key
;
16619 HeapTuple child_tuple
;
16620 bool found
= false;
16622 if (parent_con
->contype
!= CONSTRAINT_CHECK
&&
16623 parent_con
->contype
!= CONSTRAINT_NOTNULL
)
16626 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16627 if (parent_con
->connoinherit
)
16630 /* Search for a child constraint matching this one */
16631 ScanKeyInit(&child_key
,
16632 Anum_pg_constraint_conrelid
,
16633 BTEqualStrategyNumber
, F_OIDEQ
,
16634 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16635 child_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16636 true, NULL
, 1, &child_key
);
16638 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
16640 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
16641 HeapTuple child_copy
;
16643 if (child_con
->contype
!= parent_con
->contype
)
16647 * CHECK constraint are matched by name, NOT NULL ones by
16650 if (child_con
->contype
== CONSTRAINT_CHECK
)
16652 if (strcmp(NameStr(parent_con
->conname
),
16653 NameStr(child_con
->conname
)) != 0)
16656 else if (child_con
->contype
== CONSTRAINT_NOTNULL
)
16658 AttrNumber parent_attno
= extractNotNullColumn(parent_tuple
);
16659 AttrNumber child_attno
= extractNotNullColumn(child_tuple
);
16661 if (strcmp(get_attname(parent_relid
, parent_attno
, false),
16662 get_attname(RelationGetRelid(child_rel
), child_attno
,
16667 if (child_con
->contype
== CONSTRAINT_CHECK
&&
16668 !constraints_equivalent(parent_tuple
, child_tuple
, RelationGetDescr(constraintrel
)))
16670 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16671 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16672 RelationGetRelationName(child_rel
), NameStr(parent_con
->conname
))));
16675 * If the CHECK child constraint is "no inherit" then cannot
16678 * This is not desirable for not-null constraints, mostly because
16679 * it breaks our pg_upgrade strategy, but it also makes sense on
16680 * its own: if a child has its own not-null constraint and then
16681 * acquires a parent with the same constraint, then we start to
16682 * enforce that constraint for all the descendants of that child
16685 if (child_con
->contype
== CONSTRAINT_CHECK
&&
16686 child_con
->connoinherit
)
16688 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16689 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16690 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16693 * If the child constraint is "not valid" then cannot merge with a
16694 * valid parent constraint
16696 if (parent_con
->convalidated
&& !child_con
->convalidated
)
16698 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16699 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16700 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16703 * OK, bump the child constraint's inheritance count. (If we fail
16704 * later on, this change will just roll back.)
16706 child_copy
= heap_copytuple(child_tuple
);
16707 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
16708 child_con
->coninhcount
++;
16709 if (child_con
->coninhcount
< 0)
16711 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16712 errmsg("too many inheritance parents"));
16713 if (child_con
->contype
== CONSTRAINT_NOTNULL
&&
16714 child_con
->connoinherit
)
16717 * If the child has children, it's not possible to turn a NO
16718 * INHERIT constraint into an inheritable one: we would need
16719 * to recurse to create constraints in those children, but
16720 * this is not a good place to do that.
16722 if (child_rel
->rd_rel
->relhassubclass
)
16724 errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
16725 get_attname(RelationGetRelid(child_rel
),
16726 extractNotNullColumn(child_tuple
),
16728 RelationGetRelationName(child_rel
)),
16729 errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
16730 NameStr(child_con
->conname
)));
16732 child_con
->connoinherit
= false;
16736 * In case of partitions, an inherited constraint must be
16737 * inherited only once since it cannot have multiple parents and
16738 * it is never considered local.
16740 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16742 Assert(child_con
->coninhcount
== 1);
16743 child_con
->conislocal
= false;
16746 CatalogTupleUpdate(constraintrel
, &child_copy
->t_self
, child_copy
);
16747 heap_freetuple(child_copy
);
16753 systable_endscan(child_scan
);
16757 if (parent_con
->contype
== CONSTRAINT_NOTNULL
)
16759 errcode(ERRCODE_DATATYPE_MISMATCH
),
16760 errmsg("column \"%s\" in child table must be marked NOT NULL",
16761 get_attname(parent_relid
,
16762 extractNotNullColumn(parent_tuple
),
16766 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16767 errmsg("child table is missing constraint \"%s\"",
16768 NameStr(parent_con
->conname
))));
16772 systable_endscan(parent_scan
);
16773 table_close(constraintrel
, RowExclusiveLock
);
16777 * ALTER TABLE NO INHERIT
16779 * Return value is the address of the relation that is no longer parent.
16781 static ObjectAddress
16782 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16784 ObjectAddress address
;
16785 Relation parent_rel
;
16787 if (rel
->rd_rel
->relispartition
)
16789 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16790 errmsg("cannot change inheritance of a partition")));
16793 * AccessShareLock on the parent is probably enough, seeing that DROP
16794 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16795 * be inspecting the parent's schema.
16797 parent_rel
= table_openrv(parent
, AccessShareLock
);
16800 * We don't bother to check ownership of the parent table --- ownership of
16801 * the child is presumed enough rights.
16804 /* Off to RemoveInheritance() where most of the work happens */
16805 RemoveInheritance(rel
, parent_rel
, false);
16808 * If parent_rel has a primary key, then child_rel has not-null
16809 * constraints that make these columns as non nullable. Mark those
16810 * constraints as no longer inherited by this parent.
16812 ATInheritAdjustNotNulls(parent_rel
, rel
, -1);
16815 * If the parent has a primary key, then we decrement counts for all NOT
16819 ObjectAddressSet(address
, RelationRelationId
,
16820 RelationGetRelid(parent_rel
));
16822 /* keep our lock on the parent relation until commit */
16823 table_close(parent_rel
, NoLock
);
16829 * MarkInheritDetached
16831 * Set inhdetachpending for a partition, for ATExecDetachPartition
16832 * in concurrent mode. While at it, verify that no other partition is
16833 * already pending detach.
16836 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
16838 Relation catalogRelation
;
16841 HeapTuple inheritsTuple
;
16842 bool found
= false;
16844 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16847 * Find pg_inherits entries by inhparent. (We need to scan them all in
16848 * order to verify that no other partition is pending detach.)
16850 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16852 Anum_pg_inherits_inhparent
,
16853 BTEqualStrategyNumber
, F_OIDEQ
,
16854 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16855 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
16856 true, NULL
, 1, &key
);
16858 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16860 Form_pg_inherits inhForm
;
16862 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16863 if (inhForm
->inhdetachpending
)
16865 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16866 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16867 get_rel_name(inhForm
->inhrelid
),
16868 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
16869 RelationGetRelationName(parent_rel
)),
16870 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16872 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
16876 newtup
= heap_copytuple(inheritsTuple
);
16877 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
16879 CatalogTupleUpdate(catalogRelation
,
16880 &inheritsTuple
->t_self
,
16883 heap_freetuple(newtup
);
16884 /* keep looking, to ensure we catch others pending detach */
16889 systable_endscan(scan
);
16890 table_close(catalogRelation
, RowExclusiveLock
);
16894 (errcode(ERRCODE_UNDEFINED_TABLE
),
16895 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16896 RelationGetRelationName(child_rel
),
16897 RelationGetRelationName(parent_rel
))));
16901 * RemoveInheritance
16903 * Drop a parent from the child's parents. This just adjusts the attinhcount
16904 * and attislocal of the columns and removes the pg_inherit and pg_depend
16905 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16907 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16908 * up attislocal stays true, which means if a child is ever removed from a
16909 * parent then its columns will never be automatically dropped which may
16910 * surprise. But at least we'll never surprise by dropping columns someone
16911 * isn't expecting to be dropped which would actually mean data loss.
16913 * coninhcount and conislocal for inherited constraints are adjusted in
16914 * exactly the same way.
16916 * Common to ATExecDropInherit() and ATExecDetachPartition().
16919 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
16921 Relation catalogRelation
;
16923 ScanKeyData key
[3];
16924 HeapTuple attributeTuple
,
16929 bool is_partitioning
;
16931 is_partitioning
= (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16933 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
16934 RelationGetRelid(parent_rel
),
16936 RelationGetRelationName(child_rel
));
16939 if (is_partitioning
)
16941 (errcode(ERRCODE_UNDEFINED_TABLE
),
16942 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16943 RelationGetRelationName(child_rel
),
16944 RelationGetRelationName(parent_rel
))));
16947 (errcode(ERRCODE_UNDEFINED_TABLE
),
16948 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16949 RelationGetRelationName(parent_rel
),
16950 RelationGetRelationName(child_rel
))));
16954 * Search through child columns looking for ones matching parent rel
16956 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
16957 ScanKeyInit(&key
[0],
16958 Anum_pg_attribute_attrelid
,
16959 BTEqualStrategyNumber
, F_OIDEQ
,
16960 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16961 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
16962 true, NULL
, 1, key
);
16963 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
16965 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
16967 /* Ignore if dropped or not inherited */
16968 if (att
->attisdropped
)
16970 if (att
->attinhcount
<= 0)
16973 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
16974 NameStr(att
->attname
)))
16976 /* Decrement inhcount and possibly set islocal to true */
16977 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
16978 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
16980 copy_att
->attinhcount
--;
16981 if (copy_att
->attinhcount
== 0)
16982 copy_att
->attislocal
= true;
16984 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16985 heap_freetuple(copyTuple
);
16988 systable_endscan(scan
);
16989 table_close(catalogRelation
, RowExclusiveLock
);
16992 * Likewise, find inherited check constraints and disinherit them. To do
16993 * this, we first need a list of the names of the parent's check
16994 * constraints. (We cheat a bit by only checking for name matches,
16995 * assuming that the expressions will match.)
16997 * For NOT NULL columns, we store column numbers to match.
16999 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
17000 ScanKeyInit(&key
[0],
17001 Anum_pg_constraint_conrelid
,
17002 BTEqualStrategyNumber
, F_OIDEQ
,
17003 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
17004 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
17005 true, NULL
, 1, key
);
17010 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
17012 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
17014 if (con
->contype
== CONSTRAINT_CHECK
)
17015 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
17016 if (con
->contype
== CONSTRAINT_NOTNULL
)
17017 nncolumns
= lappend_int(nncolumns
, extractNotNullColumn(constraintTuple
));
17020 systable_endscan(scan
);
17022 /* Now scan the child's constraints */
17023 ScanKeyInit(&key
[0],
17024 Anum_pg_constraint_conrelid
,
17025 BTEqualStrategyNumber
, F_OIDEQ
,
17026 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
17027 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
17028 true, NULL
, 1, key
);
17030 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
17032 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
17033 bool match
= false;
17037 * Match CHECK constraints by name, not-null constraints by column
17038 * number, and ignore all others.
17040 if (con
->contype
== CONSTRAINT_CHECK
)
17042 foreach(lc
, connames
)
17044 if (con
->contype
== CONSTRAINT_CHECK
&&
17045 strcmp(NameStr(con
->conname
), (char *) lfirst(lc
)) == 0)
17052 else if (con
->contype
== CONSTRAINT_NOTNULL
)
17054 AttrNumber child_attno
= extractNotNullColumn(constraintTuple
);
17056 foreach(lc
, nncolumns
)
17058 if (lfirst_int(lc
) == child_attno
)
17070 /* Decrement inhcount and possibly set islocal to true */
17071 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
17072 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
17074 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
17075 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
17076 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
17078 copy_con
->coninhcount
--;
17079 if (copy_con
->coninhcount
== 0)
17080 copy_con
->conislocal
= true;
17082 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
17083 heap_freetuple(copyTuple
);
17087 systable_endscan(scan
);
17088 table_close(catalogRelation
, RowExclusiveLock
);
17090 drop_parent_dependency(RelationGetRelid(child_rel
),
17091 RelationRelationId
,
17092 RelationGetRelid(parent_rel
),
17093 child_dependency_type(is_partitioning
));
17096 * Post alter hook of this inherits. Since object_access_hook doesn't take
17097 * multiple object identifiers, we relay oid of parent relation using
17098 * auxiliary_id argument.
17100 InvokeObjectPostAlterHookArg(InheritsRelationId
,
17101 RelationGetRelid(child_rel
), 0,
17102 RelationGetRelid(parent_rel
), false);
17106 * Adjust coninhcount of not-null constraints upwards or downwards when a
17107 * table is marked as inheriting or no longer doing so a table with a primary
17110 * Note: these constraints are not dropped, even if their inhcount goes to zero
17111 * and conislocal is false. Instead we mark the constraints as locally defined.
17112 * This is seen as more useful behavior, with no downsides. The user can always
17113 * drop them afterwards.
17116 ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
, int inhcount
)
17118 Bitmapset
*pkattnos
;
17120 /* Quick exit when parent has no PK */
17121 if (!parent_rel
->rd_rel
->relhasindex
)
17124 pkattnos
= RelationGetIndexAttrBitmap(parent_rel
,
17125 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
17126 if (pkattnos
!= NULL
)
17128 Bitmapset
*childattnums
= NULL
;
17132 attmap
= build_attrmap_by_name(RelationGetDescr(parent_rel
),
17133 RelationGetDescr(child_rel
), true);
17136 while ((i
= bms_next_member(pkattnos
, i
)) >= 0)
17138 childattnums
= bms_add_member(childattnums
,
17139 attmap
->attnums
[i
+ FirstLowInvalidHeapAttributeNumber
- 1]);
17143 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17144 * parent: the relevant not-null constraint in the child already had
17145 * its inhcount modified earlier.
17147 CommandCounterIncrement();
17148 AdjustNotNullInheritance(RelationGetRelid(child_rel
), childattnums
,
17154 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17155 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17156 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17157 * be TypeRelationId). There's no convenient way to do this, so go trawling
17158 * through pg_depend.
17161 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
17162 DependencyType deptype
)
17164 Relation catalogRelation
;
17166 ScanKeyData key
[3];
17167 HeapTuple depTuple
;
17169 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
17171 ScanKeyInit(&key
[0],
17172 Anum_pg_depend_classid
,
17173 BTEqualStrategyNumber
, F_OIDEQ
,
17174 ObjectIdGetDatum(RelationRelationId
));
17175 ScanKeyInit(&key
[1],
17176 Anum_pg_depend_objid
,
17177 BTEqualStrategyNumber
, F_OIDEQ
,
17178 ObjectIdGetDatum(relid
));
17179 ScanKeyInit(&key
[2],
17180 Anum_pg_depend_objsubid
,
17181 BTEqualStrategyNumber
, F_INT4EQ
,
17184 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
17187 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
17189 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
17191 if (dep
->refclassid
== refclassid
&&
17192 dep
->refobjid
== refobjid
&&
17193 dep
->refobjsubid
== 0 &&
17194 dep
->deptype
== deptype
)
17195 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
17198 systable_endscan(scan
);
17199 table_close(catalogRelation
, RowExclusiveLock
);
17205 * Attach a table to a composite type, as though it had been created with CREATE
17206 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17207 * subject table must not have inheritance parents. These restrictions ensure
17208 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17210 * The address of the type is returned.
17212 static ObjectAddress
17213 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
17215 Oid relid
= RelationGetRelid(rel
);
17217 Form_pg_type typeform
;
17219 Relation inheritsRelation
,
17223 AttrNumber table_attno
,
17225 TupleDesc typeTupleDesc
,
17227 ObjectAddress tableobj
,
17229 HeapTuple classtuple
;
17231 /* Validate the type. */
17232 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
17233 check_of_type(typetuple
);
17234 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
17235 typeid = typeform
->oid
;
17237 /* Fail if the table has any inheritance parents. */
17238 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
17240 Anum_pg_inherits_inhrelid
,
17241 BTEqualStrategyNumber
, F_OIDEQ
,
17242 ObjectIdGetDatum(relid
));
17243 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
17244 true, NULL
, 1, &key
);
17245 if (HeapTupleIsValid(systable_getnext(scan
)))
17247 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17248 errmsg("typed tables cannot inherit")));
17249 systable_endscan(scan
);
17250 table_close(inheritsRelation
, AccessShareLock
);
17253 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17254 * require that the order also match. However, attnotnull need not match.
17256 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
17257 tableTupleDesc
= RelationGetDescr(rel
);
17259 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
17261 Form_pg_attribute type_attr
,
17263 const char *type_attname
,
17266 /* Get the next non-dropped type attribute. */
17267 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
17268 if (type_attr
->attisdropped
)
17270 type_attname
= NameStr(type_attr
->attname
);
17272 /* Get the next non-dropped table attribute. */
17275 if (table_attno
> tableTupleDesc
->natts
)
17277 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17278 errmsg("table is missing column \"%s\"",
17280 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
17282 } while (table_attr
->attisdropped
);
17283 table_attname
= NameStr(table_attr
->attname
);
17285 /* Compare name. */
17286 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
17288 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17289 errmsg("table has column \"%s\" where type requires \"%s\"",
17290 table_attname
, type_attname
)));
17292 /* Compare type. */
17293 if (table_attr
->atttypid
!= type_attr
->atttypid
||
17294 table_attr
->atttypmod
!= type_attr
->atttypmod
||
17295 table_attr
->attcollation
!= type_attr
->attcollation
)
17297 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17298 errmsg("table \"%s\" has different type for column \"%s\"",
17299 RelationGetRelationName(rel
), type_attname
)));
17301 ReleaseTupleDesc(typeTupleDesc
);
17303 /* Any remaining columns at the end of the table had better be dropped. */
17304 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
17306 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
17309 if (!table_attr
->attisdropped
)
17311 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17312 errmsg("table has extra column \"%s\"",
17313 NameStr(table_attr
->attname
))));
17316 /* If the table was already typed, drop the existing dependency. */
17317 if (rel
->rd_rel
->reloftype
)
17318 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17319 DEPENDENCY_NORMAL
);
17321 /* Record a dependency on the new type. */
17322 tableobj
.classId
= RelationRelationId
;
17323 tableobj
.objectId
= relid
;
17324 tableobj
.objectSubId
= 0;
17325 typeobj
.classId
= TypeRelationId
;
17326 typeobj
.objectId
= typeid;
17327 typeobj
.objectSubId
= 0;
17328 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
17330 /* Update pg_class.reloftype */
17331 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17332 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17333 if (!HeapTupleIsValid(classtuple
))
17334 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17335 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
17336 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
17338 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17340 heap_freetuple(classtuple
);
17341 table_close(relationRelation
, RowExclusiveLock
);
17343 ReleaseSysCache(typetuple
);
17349 * ALTER TABLE NOT OF
17351 * Detach a typed table from its originating type. Just clear reloftype and
17352 * remove the dependency.
17355 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
17357 Oid relid
= RelationGetRelid(rel
);
17358 Relation relationRelation
;
17361 if (!OidIsValid(rel
->rd_rel
->reloftype
))
17363 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17364 errmsg("\"%s\" is not a typed table",
17365 RelationGetRelationName(rel
))));
17368 * We don't bother to check ownership of the type --- ownership of the
17369 * table is presumed enough rights. No lock required on the type, either.
17372 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17373 DEPENDENCY_NORMAL
);
17375 /* Clear pg_class.reloftype */
17376 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17377 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17378 if (!HeapTupleIsValid(tuple
))
17379 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17380 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
17381 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
17383 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17385 heap_freetuple(tuple
);
17386 table_close(relationRelation
, RowExclusiveLock
);
17390 * relation_mark_replica_identity: Update a table's replica identity
17392 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17393 * index. Otherwise, it must be InvalidOid.
17395 * Caller had better hold an exclusive lock on the relation, as the results
17396 * of running two of these concurrently wouldn't be pretty.
17399 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
17404 HeapTuple pg_class_tuple
;
17405 HeapTuple pg_index_tuple
;
17406 Form_pg_class pg_class_form
;
17407 Form_pg_index pg_index_form
;
17411 * Check whether relreplident has changed, and update it if so.
17413 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17414 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
17415 ObjectIdGetDatum(RelationGetRelid(rel
)));
17416 if (!HeapTupleIsValid(pg_class_tuple
))
17417 elog(ERROR
, "cache lookup failed for relation \"%s\"",
17418 RelationGetRelationName(rel
));
17419 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
17420 if (pg_class_form
->relreplident
!= ri_type
)
17422 pg_class_form
->relreplident
= ri_type
;
17423 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
17425 table_close(pg_class
, RowExclusiveLock
);
17426 heap_freetuple(pg_class_tuple
);
17429 * Update the per-index indisreplident flags correctly.
17431 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
17432 foreach(index
, RelationGetIndexList(rel
))
17434 Oid thisIndexOid
= lfirst_oid(index
);
17435 bool dirty
= false;
17437 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
17438 ObjectIdGetDatum(thisIndexOid
));
17439 if (!HeapTupleIsValid(pg_index_tuple
))
17440 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
17441 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
17443 if (thisIndexOid
== indexOid
)
17445 /* Set the bit if not already set. */
17446 if (!pg_index_form
->indisreplident
)
17449 pg_index_form
->indisreplident
= true;
17454 /* Unset the bit if set. */
17455 if (pg_index_form
->indisreplident
)
17458 pg_index_form
->indisreplident
= false;
17464 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
17465 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
17466 InvalidOid
, is_internal
);
17469 * Invalidate the relcache for the table, so that after we commit
17470 * all sessions will refresh the table's replica identity index
17471 * before attempting any UPDATE or DELETE on the table. (If we
17472 * changed the table's pg_class row above, then a relcache inval
17473 * is already queued due to that; but we might not have.)
17475 CacheInvalidateRelcache(rel
);
17477 heap_freetuple(pg_index_tuple
);
17480 table_close(pg_index
, RowExclusiveLock
);
17484 * ALTER TABLE <name> REPLICA IDENTITY ...
17487 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
17493 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
17495 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17498 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
17500 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17503 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
17505 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17508 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
17510 /* fallthrough */ ;
17513 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
17515 /* Check that the index exists */
17516 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
17517 if (!OidIsValid(indexOid
))
17519 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17520 errmsg("index \"%s\" for table \"%s\" does not exist",
17521 stmt
->name
, RelationGetRelationName(rel
))));
17523 indexRel
= index_open(indexOid
, ShareLock
);
17525 /* Check that the index is on the relation we're altering. */
17526 if (indexRel
->rd_index
== NULL
||
17527 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
17529 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17530 errmsg("\"%s\" is not an index for table \"%s\"",
17531 RelationGetRelationName(indexRel
),
17532 RelationGetRelationName(rel
))));
17533 /* The AM must support uniqueness, and the index must in fact be unique. */
17534 if (!indexRel
->rd_indam
->amcanunique
||
17535 !indexRel
->rd_index
->indisunique
)
17537 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17538 errmsg("cannot use non-unique index \"%s\" as replica identity",
17539 RelationGetRelationName(indexRel
))));
17540 /* Deferred indexes are not guaranteed to be always unique. */
17541 if (!indexRel
->rd_index
->indimmediate
)
17543 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17544 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17545 RelationGetRelationName(indexRel
))));
17546 /* Expression indexes aren't supported. */
17547 if (RelationGetIndexExpressions(indexRel
) != NIL
)
17549 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17550 errmsg("cannot use expression index \"%s\" as replica identity",
17551 RelationGetRelationName(indexRel
))));
17552 /* Predicate indexes aren't supported. */
17553 if (RelationGetIndexPredicate(indexRel
) != NIL
)
17555 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17556 errmsg("cannot use partial index \"%s\" as replica identity",
17557 RelationGetRelationName(indexRel
))));
17559 /* Check index for nullable columns. */
17560 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
17562 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
17563 Form_pg_attribute attr
;
17566 * Reject any other system columns. (Going forward, we'll disallow
17567 * indexes containing such columns in the first place, but they might
17568 * exist in older branches.)
17572 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
17573 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17574 RelationGetRelationName(indexRel
), attno
)));
17576 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
17577 if (!attr
->attnotnull
)
17579 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17580 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17581 RelationGetRelationName(indexRel
),
17582 NameStr(attr
->attname
))));
17585 /* This index is suitable for use as a replica identity. Mark it. */
17586 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
17588 index_close(indexRel
, NoLock
);
17592 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17595 ATExecSetRowSecurity(Relation rel
, bool rls
)
17601 relid
= RelationGetRelid(rel
);
17603 /* Pull the record for this relation and update it */
17604 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17606 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17608 if (!HeapTupleIsValid(tuple
))
17609 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17611 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
17612 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17614 InvokeObjectPostAlterHook(RelationRelationId
,
17615 RelationGetRelid(rel
), 0);
17617 table_close(pg_class
, RowExclusiveLock
);
17618 heap_freetuple(tuple
);
17622 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17625 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
17631 relid
= RelationGetRelid(rel
);
17633 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17635 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17637 if (!HeapTupleIsValid(tuple
))
17638 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17640 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
17641 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17643 InvokeObjectPostAlterHook(RelationRelationId
,
17644 RelationGetRelid(rel
), 0);
17646 table_close(pg_class
, RowExclusiveLock
);
17647 heap_freetuple(tuple
);
17651 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17654 ATExecGenericOptions(Relation rel
, List
*options
)
17657 ForeignServer
*server
;
17658 ForeignDataWrapper
*fdw
;
17661 Datum repl_val
[Natts_pg_foreign_table
];
17662 bool repl_null
[Natts_pg_foreign_table
];
17663 bool repl_repl
[Natts_pg_foreign_table
];
17665 Form_pg_foreign_table tableform
;
17667 if (options
== NIL
)
17670 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
17672 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
17673 ObjectIdGetDatum(rel
->rd_id
));
17674 if (!HeapTupleIsValid(tuple
))
17676 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17677 errmsg("foreign table \"%s\" does not exist",
17678 RelationGetRelationName(rel
))));
17679 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
17680 server
= GetForeignServer(tableform
->ftserver
);
17681 fdw
= GetForeignDataWrapper(server
->fdwid
);
17683 memset(repl_val
, 0, sizeof(repl_val
));
17684 memset(repl_null
, false, sizeof(repl_null
));
17685 memset(repl_repl
, false, sizeof(repl_repl
));
17687 /* Extract the current options */
17688 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
17690 Anum_pg_foreign_table_ftoptions
,
17693 datum
= PointerGetDatum(NULL
);
17695 /* Transform the options */
17696 datum
= transformGenericOptions(ForeignTableRelationId
,
17699 fdw
->fdwvalidator
);
17701 if (PointerIsValid(DatumGetPointer(datum
)))
17702 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
17704 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17706 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17708 /* Everything looks good - update the tuple */
17710 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
17711 repl_val
, repl_null
, repl_repl
);
17713 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
17716 * Invalidate relcache so that all sessions will refresh any cached plans
17717 * that might depend on the old options.
17719 CacheInvalidateRelcache(rel
);
17721 InvokeObjectPostAlterHook(ForeignTableRelationId
,
17722 RelationGetRelid(rel
), 0);
17724 table_close(ftrel
, RowExclusiveLock
);
17726 heap_freetuple(tuple
);
17730 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17732 * Return value is the address of the modified column
17734 static ObjectAddress
17735 ATExecSetCompression(Relation rel
,
17736 const char *column
,
17742 Form_pg_attribute atttableform
;
17746 ObjectAddress address
;
17748 compression
= strVal(newValue
);
17750 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
17752 /* copy the cache entry so we can scribble on it below */
17753 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
17754 if (!HeapTupleIsValid(tuple
))
17756 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17757 errmsg("column \"%s\" of relation \"%s\" does not exist",
17758 column
, RelationGetRelationName(rel
))));
17760 /* prevent them from altering a system attribute */
17761 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
17762 attnum
= atttableform
->attnum
;
17765 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17766 errmsg("cannot alter system column \"%s\"", column
)));
17769 * Check that column type is compressible, then get the attribute
17770 * compression method code
17772 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
17774 /* update pg_attribute entry */
17775 atttableform
->attcompression
= cmethod
;
17776 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
17778 InvokeObjectPostAlterHook(RelationRelationId
,
17779 RelationGetRelid(rel
),
17783 * Apply the change to indexes as well (only for simple index columns,
17784 * matching behavior of index.c ConstructTupleDescriptor()).
17786 SetIndexStorageProperties(rel
, attrel
, attnum
,
17791 heap_freetuple(tuple
);
17793 table_close(attrel
, RowExclusiveLock
);
17795 /* make changes visible */
17796 CommandCounterIncrement();
17798 ObjectAddressSubSet(address
, RelationRelationId
,
17799 RelationGetRelid(rel
), attnum
);
17805 * Preparation phase for SET LOGGED/UNLOGGED
17807 * This verifies that we're not trying to change a temp table. Also,
17808 * existing foreign key constraints are checked to avoid ending up with
17809 * permanent tables referencing unlogged tables.
17811 * Return value is false if the operation is a no-op (in which case the
17812 * checks are skipped), otherwise true.
17815 ATPrepChangePersistence(Relation rel
, bool toLogged
)
17817 Relation pg_constraint
;
17820 ScanKeyData skey
[1];
17823 * Disallow changing status for a temp table. Also verify whether we can
17824 * get away with doing nothing; in such cases we don't need to run the
17825 * checks below, either.
17827 switch (rel
->rd_rel
->relpersistence
)
17829 case RELPERSISTENCE_TEMP
:
17831 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17832 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17833 RelationGetRelationName(rel
)),
17836 case RELPERSISTENCE_PERMANENT
:
17838 /* nothing to do */
17841 case RELPERSISTENCE_UNLOGGED
:
17843 /* nothing to do */
17849 * Check that the table is not part of any publication when changing to
17850 * UNLOGGED, as UNLOGGED tables can't be published.
17853 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
17855 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
17856 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17857 RelationGetRelationName(rel
)),
17858 errdetail("Unlogged relations cannot be replicated.")));
17861 * Check existing foreign key constraints to preserve the invariant that
17862 * permanent tables cannot reference unlogged ones. Self-referencing
17863 * foreign keys can safely be ignored.
17865 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
17868 * Scan conrelid if changing to permanent, else confrelid. This also
17869 * determines whether a useful index exists.
17871 ScanKeyInit(&skey
[0],
17872 toLogged
? Anum_pg_constraint_conrelid
:
17873 Anum_pg_constraint_confrelid
,
17874 BTEqualStrategyNumber
, F_OIDEQ
,
17875 ObjectIdGetDatum(RelationGetRelid(rel
)));
17876 scan
= systable_beginscan(pg_constraint
,
17877 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
17878 true, NULL
, 1, skey
);
17880 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
17882 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
17884 if (con
->contype
== CONSTRAINT_FOREIGN
)
17887 Relation foreignrel
;
17889 /* the opposite end of what we used as scankey */
17890 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
17892 /* ignore if self-referencing */
17893 if (RelationGetRelid(rel
) == foreignrelid
)
17896 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
17900 if (!RelationIsPermanent(foreignrel
))
17902 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17903 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17904 RelationGetRelationName(rel
),
17905 RelationGetRelationName(foreignrel
)),
17906 errtableconstraint(rel
, NameStr(con
->conname
))));
17910 if (RelationIsPermanent(foreignrel
))
17912 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17913 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17914 RelationGetRelationName(rel
),
17915 RelationGetRelationName(foreignrel
)),
17916 errtableconstraint(rel
, NameStr(con
->conname
))));
17919 relation_close(foreignrel
, AccessShareLock
);
17923 systable_endscan(scan
);
17925 table_close(pg_constraint
, AccessShareLock
);
17931 * Execute ALTER TABLE SET SCHEMA
17934 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
17941 ObjectAddresses
*objsMoved
;
17942 ObjectAddress myself
;
17944 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
17945 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
17946 RangeVarCallbackForAlterRelation
,
17949 if (!OidIsValid(relid
))
17952 (errmsg("relation \"%s\" does not exist, skipping",
17953 stmt
->relation
->relname
)));
17954 return InvalidObjectAddress
;
17957 rel
= relation_open(relid
, NoLock
);
17959 oldNspOid
= RelationGetNamespace(rel
);
17961 /* If it's an owned sequence, disallow moving it by itself. */
17962 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
17967 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
17968 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
17970 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17971 errmsg("cannot move an owned sequence into another schema"),
17972 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17973 RelationGetRelationName(rel
),
17974 get_rel_name(tableId
))));
17977 /* Get and lock schema OID and check its permissions. */
17978 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
17979 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
17981 /* common checks on switching namespaces */
17982 CheckSetNamespace(oldNspOid
, nspOid
);
17984 objsMoved
= new_object_addresses();
17985 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
17986 free_object_addresses(objsMoved
);
17988 ObjectAddressSet(myself
, RelationRelationId
, relid
);
17991 *oldschema
= oldNspOid
;
17993 /* close rel, but keep lock until commit */
17994 relation_close(rel
, NoLock
);
18000 * The guts of relocating a table or materialized view to another namespace:
18001 * besides moving the relation itself, its dependent objects are relocated to
18005 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
18006 ObjectAddresses
*objsMoved
)
18010 Assert(objsMoved
!= NULL
);
18012 /* OK, modify the pg_class row and pg_depend entry */
18013 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
18015 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
18016 nspOid
, true, objsMoved
);
18018 /* Fix the table's row type too, if it has one */
18019 if (OidIsValid(rel
->rd_rel
->reltype
))
18020 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
, nspOid
,
18021 false, /* isImplicitArray */
18022 false, /* ignoreDependent */
18023 false, /* errorOnTableType */
18026 /* Fix other dependent stuff */
18027 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
18028 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
18029 objsMoved
, AccessExclusiveLock
);
18030 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
18033 table_close(classRel
, RowExclusiveLock
);
18037 * The guts of relocating a relation to another namespace: fix the pg_class
18038 * entry, and the pg_depend entry if any. Caller must already have
18039 * opened and write-locked pg_class.
18042 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
18043 Oid oldNspOid
, Oid newNspOid
,
18044 bool hasDependEntry
,
18045 ObjectAddresses
*objsMoved
)
18047 HeapTuple classTup
;
18048 Form_pg_class classForm
;
18049 ObjectAddress thisobj
;
18050 bool already_done
= false;
18052 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relOid
));
18053 if (!HeapTupleIsValid(classTup
))
18054 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
18055 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
18057 Assert(classForm
->relnamespace
== oldNspOid
);
18059 thisobj
.classId
= RelationRelationId
;
18060 thisobj
.objectId
= relOid
;
18061 thisobj
.objectSubId
= 0;
18064 * If the object has already been moved, don't move it again. If it's
18065 * already in the right place, don't move it, but still fire the object
18068 already_done
= object_address_present(&thisobj
, objsMoved
);
18069 if (!already_done
&& oldNspOid
!= newNspOid
)
18071 /* check for duplicate name (more friendly than unique-index failure) */
18072 if (get_relname_relid(NameStr(classForm
->relname
),
18073 newNspOid
) != InvalidOid
)
18075 (errcode(ERRCODE_DUPLICATE_TABLE
),
18076 errmsg("relation \"%s\" already exists in schema \"%s\"",
18077 NameStr(classForm
->relname
),
18078 get_namespace_name(newNspOid
))));
18080 /* classTup is a copy, so OK to scribble on */
18081 classForm
->relnamespace
= newNspOid
;
18083 CatalogTupleUpdate(classRel
, &classTup
->t_self
, classTup
);
18085 /* Update dependency on schema if caller said so */
18086 if (hasDependEntry
&&
18087 changeDependencyFor(RelationRelationId
,
18089 NamespaceRelationId
,
18092 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
18093 NameStr(classForm
->relname
));
18097 add_exact_object_address(&thisobj
, objsMoved
);
18099 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
18102 heap_freetuple(classTup
);
18106 * Move all indexes for the specified relation to another namespace.
18108 * Note: we assume adequate permission checking was done by the caller,
18109 * and that the caller has a suitable lock on the owning relation.
18112 AlterIndexNamespaces(Relation classRel
, Relation rel
,
18113 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
18118 indexList
= RelationGetIndexList(rel
);
18120 foreach(l
, indexList
)
18122 Oid indexOid
= lfirst_oid(l
);
18123 ObjectAddress thisobj
;
18125 thisobj
.classId
= RelationRelationId
;
18126 thisobj
.objectId
= indexOid
;
18127 thisobj
.objectSubId
= 0;
18130 * Note: currently, the index will not have its own dependency on the
18131 * namespace, so we don't need to do changeDependencyFor(). There's no
18132 * row type in pg_type, either.
18134 * XXX this objsMoved test may be pointless -- surely we have a single
18135 * dependency link from a relation to each index?
18137 if (!object_address_present(&thisobj
, objsMoved
))
18139 AlterRelationNamespaceInternal(classRel
, indexOid
,
18140 oldNspOid
, newNspOid
,
18142 add_exact_object_address(&thisobj
, objsMoved
);
18146 list_free(indexList
);
18150 * Move all identity and SERIAL-column sequences of the specified relation to another
18153 * Note: we assume adequate permission checking was done by the caller,
18154 * and that the caller has a suitable lock on the owning relation.
18157 AlterSeqNamespaces(Relation classRel
, Relation rel
,
18158 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
18163 ScanKeyData key
[2];
18167 * SERIAL sequences are those having an auto dependency on one of the
18168 * table's columns (we don't care *which* column, exactly).
18170 depRel
= table_open(DependRelationId
, AccessShareLock
);
18172 ScanKeyInit(&key
[0],
18173 Anum_pg_depend_refclassid
,
18174 BTEqualStrategyNumber
, F_OIDEQ
,
18175 ObjectIdGetDatum(RelationRelationId
));
18176 ScanKeyInit(&key
[1],
18177 Anum_pg_depend_refobjid
,
18178 BTEqualStrategyNumber
, F_OIDEQ
,
18179 ObjectIdGetDatum(RelationGetRelid(rel
)));
18180 /* we leave refobjsubid unspecified */
18182 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
18185 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
18187 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
18190 /* skip dependencies other than auto dependencies on columns */
18191 if (depForm
->refobjsubid
== 0 ||
18192 depForm
->classid
!= RelationRelationId
||
18193 depForm
->objsubid
!= 0 ||
18194 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
18197 /* Use relation_open just in case it's an index */
18198 seqRel
= relation_open(depForm
->objid
, lockmode
);
18200 /* skip non-sequence relations */
18201 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
18203 /* No need to keep the lock */
18204 relation_close(seqRel
, lockmode
);
18208 /* Fix the pg_class and pg_depend entries */
18209 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
18210 oldNspOid
, newNspOid
,
18214 * Sequences used to have entries in pg_type, but no longer do. If we
18215 * ever re-instate that, we'll need to move the pg_type entry to the
18216 * new namespace, too (using AlterTypeNamespaceInternal).
18218 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
18220 /* Now we can close it. Keep the lock till end of transaction. */
18221 relation_close(seqRel
, NoLock
);
18224 systable_endscan(scan
);
18226 relation_close(depRel
, AccessShareLock
);
18231 * This code supports
18232 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18234 * Because we only support this for TEMP tables, it's sufficient to remember
18235 * the state in a backend-local data structure.
18239 * Register a newly-created relation's ON COMMIT action.
18242 register_on_commit_action(Oid relid
, OnCommitAction action
)
18245 MemoryContext oldcxt
;
18248 * We needn't bother registering the relation unless there is an ON COMMIT
18249 * action we need to take.
18251 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
18254 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
18256 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
18258 oc
->oncommit
= action
;
18259 oc
->creating_subid
= GetCurrentSubTransactionId();
18260 oc
->deleting_subid
= InvalidSubTransactionId
;
18263 * We use lcons() here so that ON COMMIT actions are processed in reverse
18264 * order of registration. That might not be essential but it seems
18267 on_commits
= lcons(oc
, on_commits
);
18269 MemoryContextSwitchTo(oldcxt
);
18273 * Unregister any ON COMMIT action when a relation is deleted.
18275 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18278 remove_on_commit_action(Oid relid
)
18282 foreach(l
, on_commits
)
18284 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18286 if (oc
->relid
== relid
)
18288 oc
->deleting_subid
= GetCurrentSubTransactionId();
18295 * Perform ON COMMIT actions.
18297 * This is invoked just before actually committing, since it's possible
18298 * to encounter errors.
18301 PreCommit_on_commit_actions(void)
18304 List
*oids_to_truncate
= NIL
;
18305 List
*oids_to_drop
= NIL
;
18307 foreach(l
, on_commits
)
18309 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18311 /* Ignore entry if already dropped in this xact */
18312 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
18315 switch (oc
->oncommit
)
18317 case ONCOMMIT_NOOP
:
18318 case ONCOMMIT_PRESERVE_ROWS
:
18319 /* Do nothing (there shouldn't be such entries, actually) */
18321 case ONCOMMIT_DELETE_ROWS
:
18324 * If this transaction hasn't accessed any temporary
18325 * relations, we can skip truncating ON COMMIT DELETE ROWS
18326 * tables, as they must still be empty.
18328 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
18329 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
18331 case ONCOMMIT_DROP
:
18332 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
18338 * Truncate relations before dropping so that all dependencies between
18339 * relations are removed after they are worked on. Doing it like this
18340 * might be a waste as it is possible that a relation being truncated will
18341 * be dropped anyway due to its parent being dropped, but this makes the
18342 * code more robust because of not having to re-check that the relation
18343 * exists at truncation time.
18345 if (oids_to_truncate
!= NIL
)
18346 heap_truncate(oids_to_truncate
);
18348 if (oids_to_drop
!= NIL
)
18350 ObjectAddresses
*targetObjects
= new_object_addresses();
18352 foreach(l
, oids_to_drop
)
18354 ObjectAddress object
;
18356 object
.classId
= RelationRelationId
;
18357 object
.objectId
= lfirst_oid(l
);
18358 object
.objectSubId
= 0;
18360 Assert(!object_address_present(&object
, targetObjects
));
18362 add_exact_object_address(&object
, targetObjects
);
18366 * Object deletion might involve toast table access (to clean up
18367 * toasted catalog entries), so ensure we have a valid snapshot.
18369 PushActiveSnapshot(GetTransactionSnapshot());
18372 * Since this is an automatic drop, rather than one directly initiated
18373 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18375 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
18376 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
18378 PopActiveSnapshot();
18380 #ifdef USE_ASSERT_CHECKING
18383 * Note that table deletion will call remove_on_commit_action, so the
18384 * entry should get marked as deleted.
18386 foreach(l
, on_commits
)
18388 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18390 if (oc
->oncommit
!= ONCOMMIT_DROP
)
18393 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
18400 * Post-commit or post-abort cleanup for ON COMMIT management.
18402 * All we do here is remove no-longer-needed OnCommitItem entries.
18404 * During commit, remove entries that were deleted during this transaction;
18405 * during abort, remove those created during this transaction.
18408 AtEOXact_on_commit_actions(bool isCommit
)
18410 ListCell
*cur_item
;
18412 foreach(cur_item
, on_commits
)
18414 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18416 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
18417 oc
->creating_subid
!= InvalidSubTransactionId
)
18419 /* cur_item must be removed */
18420 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18425 /* cur_item must be preserved */
18426 oc
->creating_subid
= InvalidSubTransactionId
;
18427 oc
->deleting_subid
= InvalidSubTransactionId
;
18433 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18435 * During subabort, we can immediately remove entries created during this
18436 * subtransaction. During subcommit, just relabel entries marked during
18437 * this subtransaction as being the parent's responsibility.
18440 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
18441 SubTransactionId parentSubid
)
18443 ListCell
*cur_item
;
18445 foreach(cur_item
, on_commits
)
18447 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18449 if (!isCommit
&& oc
->creating_subid
== mySubid
)
18451 /* cur_item must be removed */
18452 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18457 /* cur_item must be preserved */
18458 if (oc
->creating_subid
== mySubid
)
18459 oc
->creating_subid
= parentSubid
;
18460 if (oc
->deleting_subid
== mySubid
)
18461 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
18467 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18468 * the relation to be locked only if (1) it's a plain or partitioned table,
18469 * materialized view, or TOAST table and (2) the current user is the owner (or
18470 * the superuser) or has been granted MAINTAIN. This meets the
18471 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18472 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18475 RangeVarCallbackMaintainsTable(const RangeVar
*relation
,
18476 Oid relId
, Oid oldRelId
, void *arg
)
18479 AclResult aclresult
;
18481 /* Nothing to do if the relation was not found. */
18482 if (!OidIsValid(relId
))
18486 * If the relation does exist, check whether it's an index. But note that
18487 * the relation might have been dropped between the time we did the name
18488 * lookup and now. In that case, there's nothing to do.
18490 relkind
= get_rel_relkind(relId
);
18493 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
18494 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
18496 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18497 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
18499 /* Check permissions */
18500 aclresult
= pg_class_aclcheck(relId
, GetUserId(), ACL_MAINTAIN
);
18501 if (aclresult
!= ACLCHECK_OK
)
18502 aclcheck_error(aclresult
,
18503 get_relkind_objtype(get_rel_relkind(relId
)),
18504 relation
->relname
);
18508 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18511 RangeVarCallbackForTruncate(const RangeVar
*relation
,
18512 Oid relId
, Oid oldRelId
, void *arg
)
18516 /* Nothing to do if the relation was not found. */
18517 if (!OidIsValid(relId
))
18520 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18521 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18522 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18524 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18525 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18527 ReleaseSysCache(tuple
);
18531 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18532 * the owner of the relation, or superuser.
18535 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
18536 Oid relId
, Oid oldRelId
, void *arg
)
18540 /* Nothing to do if the relation was not found. */
18541 if (!OidIsValid(relId
))
18544 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18545 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18546 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18548 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
18549 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
18550 relation
->relname
);
18552 if (!allowSystemTableMods
&&
18553 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
18555 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18556 errmsg("permission denied: \"%s\" is a system catalog",
18557 relation
->relname
)));
18559 ReleaseSysCache(tuple
);
18563 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18567 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
18570 Node
*stmt
= (Node
*) arg
;
18571 ObjectType reltype
;
18573 Form_pg_class classform
;
18574 AclResult aclresult
;
18577 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
18578 if (!HeapTupleIsValid(tuple
))
18579 return; /* concurrently dropped */
18580 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
18581 relkind
= classform
->relkind
;
18583 /* Must own relation. */
18584 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
18585 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
18587 /* No system table modifications unless explicitly allowed. */
18588 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
18590 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18591 errmsg("permission denied: \"%s\" is a system catalog",
18595 * Extract the specified relation type from the statement parse tree.
18597 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18598 * have CREATE rights on the containing namespace.
18600 if (IsA(stmt
, RenameStmt
))
18602 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
18603 GetUserId(), ACL_CREATE
);
18604 if (aclresult
!= ACLCHECK_OK
)
18605 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
18606 get_namespace_name(classform
->relnamespace
));
18607 reltype
= ((RenameStmt
*) stmt
)->renameType
;
18609 else if (IsA(stmt
, AlterObjectSchemaStmt
))
18610 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
18612 else if (IsA(stmt
, AlterTableStmt
))
18613 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
18616 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
18617 reltype
= OBJECT_TABLE
; /* placate compiler */
18621 * For compatibility with prior releases, we allow ALTER TABLE to be used
18622 * with most other types of relations (but not composite types). We allow
18623 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18624 * otherwise. Otherwise, the user must select the correct form of the
18625 * command for the relation at issue.
18627 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
18629 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18630 errmsg("\"%s\" is not a sequence", rv
->relname
)));
18632 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
18634 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18635 errmsg("\"%s\" is not a view", rv
->relname
)));
18637 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
18639 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18640 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
18642 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
18644 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18645 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
18647 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
18649 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18650 errmsg("\"%s\" is not a composite type", rv
->relname
)));
18652 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
18653 relkind
!= RELKIND_PARTITIONED_INDEX
18654 && !IsA(stmt
, RenameStmt
))
18656 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18657 errmsg("\"%s\" is not an index", rv
->relname
)));
18660 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18663 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
18665 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18666 errmsg("\"%s\" is a composite type", rv
->relname
),
18667 /* translator: %s is an SQL ALTER command */
18668 errhint("Use %s instead.",
18672 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18673 * to a different schema, such as indexes and TOAST tables.
18675 if (IsA(stmt
, AlterObjectSchemaStmt
))
18677 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
18679 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18680 errmsg("cannot change schema of index \"%s\"",
18682 errhint("Change the schema of the table instead.")));
18683 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
18685 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18686 errmsg("cannot change schema of composite type \"%s\"",
18688 /* translator: %s is an SQL ALTER command */
18689 errhint("Use %s instead.",
18691 else if (relkind
== RELKIND_TOASTVALUE
)
18693 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18694 errmsg("cannot change schema of TOAST table \"%s\"",
18696 errhint("Change the schema of the table instead.")));
18699 ReleaseSysCache(tuple
);
18703 * Transform any expressions present in the partition key
18705 * Returns a transformed PartitionSpec.
18707 static PartitionSpec
*
18708 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
18710 PartitionSpec
*newspec
;
18711 ParseState
*pstate
;
18712 ParseNamespaceItem
*nsitem
;
18715 newspec
= makeNode(PartitionSpec
);
18717 newspec
->strategy
= partspec
->strategy
;
18718 newspec
->partParams
= NIL
;
18719 newspec
->location
= partspec
->location
;
18721 /* Check valid number of columns for strategy */
18722 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
18723 list_length(partspec
->partParams
) != 1)
18725 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18726 errmsg("cannot use \"list\" partition strategy with more than one column")));
18729 * Create a dummy ParseState and insert the target relation as its sole
18730 * rangetable entry. We need a ParseState for transformExpr.
18732 pstate
= make_parsestate(NULL
);
18733 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
18734 NULL
, false, true);
18735 addNSItemToQuery(pstate
, nsitem
, true, true, true);
18737 /* take care of any partition expressions */
18738 foreach(l
, partspec
->partParams
)
18740 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
18744 /* Copy, to avoid scribbling on the input */
18745 pelem
= copyObject(pelem
);
18747 /* Now do parse transformation of the expression */
18748 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
18749 EXPR_KIND_PARTITION_EXPRESSION
);
18751 /* we have to fix its collations too */
18752 assign_expr_collations(pstate
, pelem
->expr
);
18755 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
18762 * Compute per-partition-column information from a list of PartitionElems.
18763 * Expressions in the PartitionElems must be parse-analyzed already.
18766 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
18767 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
18768 PartitionStrategy strategy
)
18775 foreach(lc
, partParams
)
18777 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
18781 if (pelem
->name
!= NULL
)
18783 /* Simple attribute reference */
18784 HeapTuple atttuple
;
18785 Form_pg_attribute attform
;
18787 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
18789 if (!HeapTupleIsValid(atttuple
))
18791 (errcode(ERRCODE_UNDEFINED_COLUMN
),
18792 errmsg("column \"%s\" named in partition key does not exist",
18794 parser_errposition(pstate
, pelem
->location
)));
18795 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
18797 if (attform
->attnum
<= 0)
18799 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18800 errmsg("cannot use system column \"%s\" in partition key",
18802 parser_errposition(pstate
, pelem
->location
)));
18805 * Generated columns cannot work: They are computed after BEFORE
18806 * triggers, but partition routing is done before all triggers.
18808 if (attform
->attgenerated
)
18810 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18811 errmsg("cannot use generated column in partition key"),
18812 errdetail("Column \"%s\" is a generated column.",
18814 parser_errposition(pstate
, pelem
->location
)));
18816 partattrs
[attn
] = attform
->attnum
;
18817 atttype
= attform
->atttypid
;
18818 attcollation
= attform
->attcollation
;
18819 ReleaseSysCache(atttuple
);
18824 Node
*expr
= pelem
->expr
;
18825 char partattname
[16];
18827 Assert(expr
!= NULL
);
18828 atttype
= exprType(expr
);
18829 attcollation
= exprCollation(expr
);
18832 * The expression must be of a storable type (e.g., not RECORD).
18833 * The test is the same as for whether a table column is of a safe
18834 * type (which is why we needn't check for the non-expression
18837 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
18838 CheckAttributeType(partattname
,
18839 atttype
, attcollation
,
18840 NIL
, CHKATYPE_IS_PARTKEY
);
18843 * Strip any top-level COLLATE clause. This ensures that we treat
18844 * "x COLLATE y" and "(x COLLATE y)" alike.
18846 while (IsA(expr
, CollateExpr
))
18847 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
18849 if (IsA(expr
, Var
) &&
18850 ((Var
*) expr
)->varattno
> 0)
18853 * User wrote "(column)" or "(column COLLATE something)".
18854 * Treat it like simple attribute anyway.
18856 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
18860 Bitmapset
*expr_attrs
= NULL
;
18863 partattrs
[attn
] = 0; /* marks the column as expression */
18864 *partexprs
= lappend(*partexprs
, expr
);
18867 * transformPartitionSpec() should have already rejected
18868 * subqueries, aggregates, window functions, and SRFs, based
18869 * on the EXPR_KIND_ for partition expressions.
18873 * Cannot allow system column references, since that would
18874 * make partition routing impossible: their values won't be
18875 * known yet when we need to do that.
18877 pull_varattnos(expr
, 1, &expr_attrs
);
18878 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
18880 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
18883 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18884 errmsg("partition key expressions cannot contain system column references")));
18888 * Generated columns cannot work: They are computed after
18889 * BEFORE triggers, but partition routing is done before all
18893 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
18895 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
18898 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
18900 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18901 errmsg("cannot use generated column in partition key"),
18902 errdetail("Column \"%s\" is a generated column.",
18903 get_attname(RelationGetRelid(rel
), attno
, false)),
18904 parser_errposition(pstate
, pelem
->location
)));
18908 * Preprocess the expression before checking for mutability.
18909 * This is essential for the reasons described in
18910 * contain_mutable_functions_after_planning. However, we call
18911 * expression_planner for ourselves rather than using that
18912 * function, because if constant-folding reduces the
18913 * expression to a constant, we'd like to know that so we can
18916 * Like contain_mutable_functions_after_planning, assume that
18917 * expression_planner won't scribble on its input, so this
18918 * won't affect the partexprs entry we saved above.
18920 expr
= (Node
*) expression_planner((Expr
*) expr
);
18923 * Partition expressions cannot contain mutable functions,
18924 * because a given row must always map to the same partition
18925 * as long as there is no change in the partition boundary
18928 if (contain_mutable_functions(expr
))
18930 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18931 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18934 * While it is not exactly *wrong* for a partition expression
18935 * to be a constant, it seems better to reject such keys.
18937 if (IsA(expr
, Const
))
18939 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18940 errmsg("cannot use constant expression as partition key")));
18945 * Apply collation override if any
18947 if (pelem
->collation
)
18948 attcollation
= get_collation_oid(pelem
->collation
, false);
18951 * Check we have a collation iff it's a collatable type. The only
18952 * expected failures here are (1) COLLATE applied to a noncollatable
18953 * type, or (2) partition expression had an unresolved collation. But
18954 * we might as well code this to be a complete consistency check.
18956 if (type_is_collatable(atttype
))
18958 if (!OidIsValid(attcollation
))
18960 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
18961 errmsg("could not determine which collation to use for partition expression"),
18962 errhint("Use the COLLATE clause to set the collation explicitly.")));
18966 if (OidIsValid(attcollation
))
18968 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18969 errmsg("collations are not supported by type %s",
18970 format_type_be(atttype
))));
18973 partcollation
[attn
] = attcollation
;
18976 * Identify the appropriate operator class. For list and range
18977 * partitioning, we use a btree operator class; hash partitioning uses
18978 * a hash operator class.
18980 if (strategy
== PARTITION_STRATEGY_HASH
)
18981 am_oid
= HASH_AM_OID
;
18983 am_oid
= BTREE_AM_OID
;
18985 if (!pelem
->opclass
)
18987 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
18989 if (!OidIsValid(partopclass
[attn
]))
18991 if (strategy
== PARTITION_STRATEGY_HASH
)
18993 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18994 errmsg("data type %s has no default operator class for access method \"%s\"",
18995 format_type_be(atttype
), "hash"),
18996 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18999 (errcode(ERRCODE_UNDEFINED_OBJECT
),
19000 errmsg("data type %s has no default operator class for access method \"%s\"",
19001 format_type_be(atttype
), "btree"),
19002 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19006 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
19008 am_oid
== HASH_AM_OID
? "hash" : "btree",
19016 * PartConstraintImpliedByRelConstraint
19017 * Do scanrel's existing constraints imply the partition constraint?
19019 * "Existing constraints" include its check constraints and column-level
19020 * not-null constraints. partConstraint describes the partition constraint,
19021 * in implicit-AND form.
19024 PartConstraintImpliedByRelConstraint(Relation scanrel
,
19025 List
*partConstraint
)
19027 List
*existConstraint
= NIL
;
19028 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
19031 if (constr
&& constr
->has_not_null
)
19033 int natts
= scanrel
->rd_att
->natts
;
19035 for (i
= 1; i
<= natts
; i
++)
19037 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
19039 if (att
->attnotnull
&& !att
->attisdropped
)
19041 NullTest
*ntest
= makeNode(NullTest
);
19043 ntest
->arg
= (Expr
*) makeVar(1,
19049 ntest
->nulltesttype
= IS_NOT_NULL
;
19052 * argisrow=false is correct even for a composite column,
19053 * because attnotnull does not represent a SQL-spec IS NOT
19054 * NULL test in such a case, just IS DISTINCT FROM NULL.
19056 ntest
->argisrow
= false;
19057 ntest
->location
= -1;
19058 existConstraint
= lappend(existConstraint
, ntest
);
19063 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
19067 * ConstraintImpliedByRelConstraint
19068 * Do scanrel's existing constraints imply the given constraint?
19070 * testConstraint is the constraint to validate. provenConstraint is a
19071 * caller-provided list of conditions which this function may assume
19072 * to be true. Both provenConstraint and testConstraint must be in
19073 * implicit-AND form, must only contain immutable clauses, and must
19074 * contain only Vars with varno = 1.
19077 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
19079 List
*existConstraint
= list_copy(provenConstraint
);
19080 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
19084 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
19085 for (i
= 0; i
< num_check
; i
++)
19090 * If this constraint hasn't been fully validated yet, we must ignore
19093 if (!constr
->check
[i
].ccvalid
)
19096 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
19099 * Run each expression through const-simplification and
19100 * canonicalization. It is necessary, because we will be comparing it
19101 * to similarly-processed partition constraint expressions, and may
19102 * fail to detect valid matches without this.
19104 cexpr
= eval_const_expressions(NULL
, cexpr
);
19105 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
19107 existConstraint
= list_concat(existConstraint
,
19108 make_ands_implicit((Expr
*) cexpr
));
19112 * Try to make the proof. Since we are comparing CHECK constraints, we
19113 * need to use weak implication, i.e., we assume existConstraint is
19114 * not-false and try to prove the same for testConstraint.
19116 * Note that predicate_implied_by assumes its first argument is known
19117 * immutable. That should always be true for both NOT NULL and partition
19118 * constraints, so we don't test it here.
19120 return predicate_implied_by(testConstraint
, existConstraint
, true);
19124 * QueuePartitionConstraintValidation
19126 * Add an entry to wqueue to have the given partition constraint validated by
19127 * Phase 3, for the given relation, and all its children.
19129 * We first verify whether the given constraint is implied by pre-existing
19130 * relation constraints; if it is, there's no need to scan the table to
19131 * validate, so don't queue in that case.
19134 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
19135 List
*partConstraint
,
19136 bool validate_default
)
19139 * Based on the table's existing constraints, determine whether or not we
19140 * may skip scanning the table.
19142 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
19144 if (!validate_default
)
19146 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19147 RelationGetRelationName(scanrel
))));
19150 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19151 RelationGetRelationName(scanrel
))));
19156 * Constraints proved insufficient. For plain relations, queue a
19157 * validation item now; for partitioned tables, recurse to process each
19160 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
19162 AlteredTableInfo
*tab
;
19164 /* Grab a work queue entry. */
19165 tab
= ATGetQueueEntry(wqueue
, scanrel
);
19166 Assert(tab
->partition_constraint
== NULL
);
19167 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
19168 tab
->validate_default
= validate_default
;
19170 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19172 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
19175 for (i
= 0; i
< partdesc
->nparts
; i
++)
19178 List
*thisPartConstraint
;
19181 * This is the minimum lock we need to prevent deadlocks.
19183 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
19186 * Adjust the constraint for scanrel so that it matches this
19187 * partition's attribute numbers.
19189 thisPartConstraint
=
19190 map_partition_varattnos(partConstraint
, 1,
19191 part_rel
, scanrel
);
19193 QueuePartitionConstraintValidation(wqueue
, part_rel
,
19194 thisPartConstraint
,
19196 table_close(part_rel
, NoLock
); /* keep lock till commit */
19202 * attachPartitionTable: attach a new partition to the partitioned table
19204 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
19205 * of an ALTER TABLE sequence.
19206 * rel: partitioned relation;
19207 * attachrel: relation of attached partition;
19208 * bound: bounds of attached relation.
19211 attachPartitionTable(List
**wqueue
, Relation rel
, Relation attachrel
, PartitionBoundSpec
*bound
)
19213 /* OK to create inheritance. Rest of the checks performed there */
19214 CreateInheritance(attachrel
, rel
, true);
19216 /* Update the pg_class entry. */
19217 StorePartitionBound(attachrel
, rel
, bound
);
19219 /* Ensure there exists a correct set of indexes in the partition. */
19220 AttachPartitionEnsureIndexes(wqueue
, rel
, attachrel
);
19223 CloneRowTriggersToPartition(rel
, attachrel
);
19226 * Clone foreign key constraints. Callee is responsible for setting up
19227 * for phase 3 constraint verification.
19229 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
19233 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19235 * Return the address of the newly attached partition.
19237 static ObjectAddress
19238 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
19239 AlterTableUtilityContext
*context
)
19241 Relation attachrel
,
19243 List
*attachrel_children
;
19244 List
*partConstraint
;
19249 TupleDesc tupleDesc
;
19250 ObjectAddress address
;
19251 const char *trigger_name
;
19252 Oid defaultPartOid
;
19253 List
*partBoundConstraint
;
19254 ParseState
*pstate
= make_parsestate(NULL
);
19256 pstate
->p_sourcetext
= context
->queryString
;
19259 * We must lock the default partition if one exists, because attaching a
19260 * new partition will change its partition constraint.
19263 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19264 if (OidIsValid(defaultPartOid
))
19265 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19267 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
19270 * XXX I think it'd be a good idea to grab locks on all tables referenced
19271 * by FKs at this point also.
19275 * Must be owner of both parent and source table -- parent was checked by
19276 * ATSimplePermissions call in ATPrepCmd
19278 ATSimplePermissions(AT_AttachPartition
, attachrel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
19280 /* A partition can only have one parent */
19281 if (attachrel
->rd_rel
->relispartition
)
19283 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19284 errmsg("\"%s\" is already a partition",
19285 RelationGetRelationName(attachrel
))));
19287 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
19289 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19290 errmsg("cannot attach a typed table as partition")));
19293 * Table being attached should not already be part of inheritance; either
19294 * as a child table...
19296 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
19298 Anum_pg_inherits_inhrelid
,
19299 BTEqualStrategyNumber
, F_OIDEQ
,
19300 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19301 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
19303 if (HeapTupleIsValid(systable_getnext(scan
)))
19305 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19306 errmsg("cannot attach inheritance child as partition")));
19307 systable_endscan(scan
);
19309 /* ...or as a parent table (except the case when it is partitioned) */
19311 Anum_pg_inherits_inhparent
,
19312 BTEqualStrategyNumber
, F_OIDEQ
,
19313 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19314 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
19316 if (HeapTupleIsValid(systable_getnext(scan
)) &&
19317 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
19319 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19320 errmsg("cannot attach inheritance parent as partition")));
19321 systable_endscan(scan
);
19322 table_close(catalog
, AccessShareLock
);
19325 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19326 * particular, this disallows making a rel a partition of itself.)
19328 * We do that by checking if rel is a member of the list of attachrel's
19329 * partitions provided the latter is partitioned at all. We want to avoid
19330 * having to construct this list again, so we request the strongest lock
19331 * on all partitions. We need the strongest lock, because we may decide
19332 * to scan them if we find out that the table being attached (or its leaf
19333 * partitions) may contain rows that violate the partition constraint. If
19334 * the table has a constraint that would prevent such rows, which by
19335 * definition is present in all the partitions, we need not scan the
19336 * table, nor its partitions. But we cannot risk a deadlock by taking a
19337 * weaker lock now and the stronger one only when needed.
19339 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
19340 AccessExclusiveLock
, NULL
);
19341 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
19343 (errcode(ERRCODE_DUPLICATE_TABLE
),
19344 errmsg("circular inheritance not allowed"),
19345 errdetail("\"%s\" is already a child of \"%s\".",
19346 RelationGetRelationName(rel
),
19347 RelationGetRelationName(attachrel
))));
19349 /* If the parent is permanent, so must be all of its partitions. */
19350 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
19351 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
19353 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19354 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19355 RelationGetRelationName(rel
))));
19357 /* Temp parent cannot have a partition that is itself not a temp */
19358 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19359 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
19361 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19362 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19363 RelationGetRelationName(rel
))));
19365 /* If the parent is temp, it must belong to this session */
19366 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19367 !rel
->rd_islocaltemp
)
19369 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19370 errmsg("cannot attach as partition of temporary relation of another session")));
19372 /* Ditto for the partition */
19373 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19374 !attachrel
->rd_islocaltemp
)
19376 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19377 errmsg("cannot attach temporary relation of another session as partition")));
19380 * Check if attachrel has any identity columns or any columns that aren't
19383 tupleDesc
= RelationGetDescr(attachrel
);
19384 natts
= tupleDesc
->natts
;
19385 for (attno
= 1; attno
<= natts
; attno
++)
19387 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
19388 char *attributeName
= NameStr(attribute
->attname
);
19390 /* Ignore dropped */
19391 if (attribute
->attisdropped
)
19394 if (attribute
->attidentity
)
19396 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19397 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19398 RelationGetRelationName(attachrel
), attributeName
),
19399 errdetail("The new partition may not contain an identity column."));
19401 /* Try to find the column in parent (matching on column name) */
19402 if (!SearchSysCacheExists2(ATTNAME
,
19403 ObjectIdGetDatum(RelationGetRelid(rel
)),
19404 CStringGetDatum(attributeName
)))
19406 (errcode(ERRCODE_DATATYPE_MISMATCH
),
19407 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19408 RelationGetRelationName(attachrel
), attributeName
,
19409 RelationGetRelationName(rel
)),
19410 errdetail("The new partition may contain only the columns present in parent.")));
19414 * If child_rel has row-level triggers with transition tables, we
19415 * currently don't allow it to become a partition. See also prohibitions
19416 * in ATExecAddInherit() and CreateTrigger().
19418 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
19419 if (trigger_name
!= NULL
)
19421 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
19422 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19423 trigger_name
, RelationGetRelationName(attachrel
)),
19424 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19427 * Check that the new partition's bound is valid and does not overlap any
19428 * of existing partitions of the parent - note that it does not return on
19431 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
19432 cmd
->bound
, pstate
);
19434 /* Attach a new partition to the partitioned table. */
19435 attachPartitionTable(wqueue
, rel
, attachrel
, cmd
->bound
);
19438 * Generate partition constraint from the partition bound specification.
19439 * If the parent itself is a partition, make sure to include its
19440 * constraint as well.
19442 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
19443 partConstraint
= list_concat(partBoundConstraint
,
19444 RelationGetPartitionQual(rel
));
19446 /* Skip validation if there are no constraints to validate. */
19447 if (partConstraint
)
19450 * Run the partition quals through const-simplification similar to
19451 * check constraints. We skip canonicalize_qual, though, because
19452 * partition quals should be in canonical form already.
19455 (List
*) eval_const_expressions(NULL
,
19456 (Node
*) partConstraint
);
19458 /* XXX this sure looks wrong */
19459 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
19462 * Adjust the generated constraint to match this partition's attribute
19465 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
19468 /* Validate partition constraints against the table being attached. */
19469 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
19474 * If we're attaching a partition other than the default partition and a
19475 * default one exists, then that partition's partition constraint changes,
19476 * so add an entry to the work queue to validate it, too. (We must not do
19477 * this when the partition being attached is the default one; we already
19480 if (OidIsValid(defaultPartOid
))
19482 Relation defaultrel
;
19483 List
*defPartConstraint
;
19485 Assert(!cmd
->bound
->is_default
);
19487 /* we already hold a lock on the default partition */
19488 defaultrel
= table_open(defaultPartOid
, NoLock
);
19489 defPartConstraint
=
19490 get_proposed_default_constraint(partBoundConstraint
);
19493 * Map the Vars in the constraint expression from rel's attnos to
19496 defPartConstraint
=
19497 map_partition_varattnos(defPartConstraint
,
19498 1, defaultrel
, rel
);
19499 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
19500 defPartConstraint
, true);
19502 /* keep our lock until commit. */
19503 table_close(defaultrel
, NoLock
);
19506 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
19509 * If the partition we just attached is partitioned itself, invalidate
19510 * relcache for all descendent partitions too to ensure that their
19511 * rd_partcheck expression trees are rebuilt; partitions already locked at
19512 * the beginning of this function.
19514 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19518 foreach(l
, attachrel_children
)
19520 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
19524 /* keep our lock until commit */
19525 table_close(attachrel
, NoLock
);
19531 * AttachPartitionEnsureIndexes
19532 * subroutine for ATExecAttachPartition to create/match indexes
19534 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19535 * PARTITION: every partition must have an index attached to each index on the
19536 * partitioned table.
19539 AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
)
19542 List
*attachRelIdxs
;
19543 Relation
*attachrelIdxRels
;
19544 IndexInfo
**attachInfos
;
19547 MemoryContext oldcxt
;
19549 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
19550 "AttachPartitionEnsureIndexes",
19551 ALLOCSET_DEFAULT_SIZES
);
19552 oldcxt
= MemoryContextSwitchTo(cxt
);
19554 idxes
= RelationGetIndexList(rel
);
19555 attachRelIdxs
= RelationGetIndexList(attachrel
);
19556 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
19557 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
19559 /* Build arrays of all existing indexes and their IndexInfos */
19560 foreach(cell
, attachRelIdxs
)
19562 Oid cldIdxId
= lfirst_oid(cell
);
19563 int i
= foreach_current_index(cell
);
19565 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
19566 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
19570 * If we're attaching a foreign table, we must fail if any of the indexes
19571 * is a constraint index; otherwise, there's nothing to do here. Do this
19572 * before starting work, to avoid wasting the effort of building a few
19573 * non-unique indexes before coming across a unique one.
19575 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
19577 foreach(cell
, idxes
)
19579 Oid idx
= lfirst_oid(cell
);
19580 Relation idxRel
= index_open(idx
, AccessShareLock
);
19582 if (idxRel
->rd_index
->indisunique
||
19583 idxRel
->rd_index
->indisprimary
)
19585 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19586 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19587 RelationGetRelationName(attachrel
),
19588 RelationGetRelationName(rel
)),
19589 errdetail("Partitioned table \"%s\" contains unique indexes.",
19590 RelationGetRelationName(rel
))));
19591 index_close(idxRel
, AccessShareLock
);
19598 * For each index on the partitioned table, find a matching one in the
19599 * partition-to-be; if one is not found, create one.
19601 foreach(cell
, idxes
)
19603 Oid idx
= lfirst_oid(cell
);
19604 Relation idxRel
= index_open(idx
, AccessShareLock
);
19607 bool found
= false;
19611 * Ignore indexes in the partitioned table other than partitioned
19614 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
19616 index_close(idxRel
, AccessShareLock
);
19620 /* construct an indexinfo to compare existing indexes against */
19621 info
= BuildIndexInfo(idxRel
);
19622 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
19623 RelationGetDescr(rel
),
19625 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
19628 * Scan the list of existing indexes in the partition-to-be, and mark
19629 * the first matching, valid, unattached one we find, if any, as
19630 * partition of the parent index. If we find one, we're done.
19632 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19634 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
19635 Oid cldConstrOid
= InvalidOid
;
19637 /* does this index have a parent? if so, can't use it */
19638 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
19641 /* If this index is invalid, can't use it */
19642 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
19645 if (CompareIndexInfo(attachInfos
[i
], info
,
19646 attachrelIdxRels
[i
]->rd_indcollation
,
19647 idxRel
->rd_indcollation
,
19648 attachrelIdxRels
[i
]->rd_opfamily
,
19649 idxRel
->rd_opfamily
,
19653 * If this index is being created in the parent because of a
19654 * constraint, then the child needs to have a constraint also,
19655 * so look for one. If there is no such constraint, this
19656 * index is no good, so keep looking.
19658 if (OidIsValid(constraintOid
))
19661 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
19664 if (!OidIsValid(cldConstrOid
))
19667 /* Ensure they're both the same type of constraint */
19668 if (get_constraint_type(constraintOid
) !=
19669 get_constraint_type(cldConstrOid
))
19674 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
19675 if (OidIsValid(constraintOid
))
19676 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
19677 RelationGetRelid(attachrel
));
19680 CommandCounterIncrement();
19686 * If no suitable index was found in the partition-to-be, create one
19694 stmt
= generateClonedIndexStmt(NULL
,
19699 * If the index is a primary key, mark all columns as NOT NULL if
19700 * they aren't already.
19704 MemoryContextSwitchTo(oldcxt
);
19705 for (int j
= 0; j
< info
->ii_NumIndexKeyAttrs
; j
++)
19707 AttrNumber childattno
;
19709 childattno
= get_attnum(RelationGetRelid(attachrel
),
19710 get_attname(RelationGetRelid(rel
),
19711 info
->ii_IndexAttrNumbers
[j
],
19713 set_attnotnull(wqueue
, attachrel
, childattno
,
19714 true, AccessExclusiveLock
);
19716 MemoryContextSwitchTo(cxt
);
19719 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
19720 RelationGetRelid(idxRel
),
19723 true, false, false, false, false);
19726 index_close(idxRel
, AccessShareLock
);
19731 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19732 index_close(attachrelIdxRels
[i
], AccessShareLock
);
19733 MemoryContextSwitchTo(oldcxt
);
19734 MemoryContextDelete(cxt
);
19738 * CloneRowTriggersToPartition
19739 * subroutine for ATExecAttachPartition/DefineRelation to create row
19740 * triggers on partitions
19743 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
19745 Relation pg_trigger
;
19749 MemoryContext perTupCxt
;
19751 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19752 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
19753 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
19754 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
19755 true, NULL
, 1, &key
);
19757 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
19758 "clone trig", ALLOCSET_SMALL_SIZES
);
19760 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
19762 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
19763 CreateTrigStmt
*trigStmt
;
19768 List
*trigargs
= NIL
;
19769 MemoryContext oldcxt
;
19772 * Ignore statement-level triggers; those are not cloned.
19774 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
19778 * Don't clone internal triggers, because the constraint cloning code
19781 if (trigForm
->tgisinternal
)
19785 * Complain if we find an unexpected trigger type.
19787 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
19788 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
19789 elog(ERROR
, "unexpected trigger \"%s\" found",
19790 NameStr(trigForm
->tgname
));
19792 /* Use short-lived context for CREATE TRIGGER */
19793 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
19796 * If there is a WHEN clause, generate a 'cooked' version of it that's
19797 * appropriate for the partition.
19799 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
19800 RelationGetDescr(pg_trigger
), &isnull
);
19803 qual
= stringToNode(TextDatumGetCString(value
));
19804 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
19805 partition
, parent
);
19806 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
19807 partition
, parent
);
19811 * If there is a column list, transform it to a list of column names.
19812 * Note we don't need to map this list in any way ...
19814 if (trigForm
->tgattr
.dim1
> 0)
19818 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
19820 Form_pg_attribute col
;
19822 col
= TupleDescAttr(parent
->rd_att
,
19823 trigForm
->tgattr
.values
[i
] - 1);
19824 cols
= lappend(cols
,
19825 makeString(pstrdup(NameStr(col
->attname
))));
19829 /* Reconstruct trigger arguments list. */
19830 if (trigForm
->tgnargs
> 0)
19834 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
19835 RelationGetDescr(pg_trigger
), &isnull
);
19837 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19838 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
19840 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
19842 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
19844 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
19845 p
+= strlen(p
) + 1;
19849 trigStmt
= makeNode(CreateTrigStmt
);
19850 trigStmt
->replace
= false;
19851 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
19852 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
19853 trigStmt
->relation
= NULL
;
19854 trigStmt
->funcname
= NULL
; /* passed separately */
19855 trigStmt
->args
= trigargs
;
19856 trigStmt
->row
= true;
19857 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
19858 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
19859 trigStmt
->columns
= cols
;
19860 trigStmt
->whenClause
= NULL
; /* passed separately */
19861 trigStmt
->transitionRels
= NIL
; /* not supported at present */
19862 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
19863 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
19864 trigStmt
->constrrel
= NULL
; /* passed separately */
19866 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
19867 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
19868 trigForm
->tgfoid
, trigForm
->oid
, qual
,
19869 false, true, trigForm
->tgenabled
);
19871 MemoryContextSwitchTo(oldcxt
);
19872 MemoryContextReset(perTupCxt
);
19875 MemoryContextDelete(perTupCxt
);
19877 systable_endscan(scan
);
19878 table_close(pg_trigger
, RowExclusiveLock
);
19882 * ALTER TABLE DETACH PARTITION
19884 * Return the address of the relation that is no longer a partition of rel.
19886 * If concurrent mode is requested, we run in two transactions. A side-
19887 * effect is that this command cannot run in a multi-part ALTER TABLE.
19888 * Currently, that's enforced by the grammar.
19890 * The strategy for concurrency is to first modify the partition's
19891 * pg_inherit catalog row to make it visible to everyone that the
19892 * partition is detached, lock the partition against writes, and commit
19893 * the transaction; anyone who requests the partition descriptor from
19894 * that point onwards has to ignore such a partition. In a second
19895 * transaction, we wait until all transactions that could have seen the
19896 * partition as attached are gone, then we remove the rest of partition
19897 * metadata (pg_inherits and pg_class.relpartbounds).
19899 static ObjectAddress
19900 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
19901 RangeVar
*name
, bool concurrent
)
19904 ObjectAddress address
;
19905 Oid defaultPartOid
;
19908 * We must lock the default partition, because detaching this partition
19909 * will change its partition constraint.
19912 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19913 if (OidIsValid(defaultPartOid
))
19916 * Concurrent detaching when a default partition exists is not
19917 * supported. The main problem is that the default partition
19918 * constraint would change. And there's a definitional problem: what
19919 * should happen to the tuples that are being inserted that belong to
19920 * the partition being detached? Putting them on the partition being
19921 * detached would be wrong, since they'd become "lost" after the
19922 * detaching completes but we cannot put them in the default partition
19923 * either until we alter its partition constraint.
19925 * I think we could solve this problem if we effected the constraint
19926 * change before committing the first transaction. But the lock would
19927 * have to remain AEL and it would cause concurrent query planning to
19928 * be blocked, so changing it that way would be even worse.
19932 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19933 errmsg("cannot detach partitions concurrently when a default partition exists")));
19934 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19938 * In concurrent mode, the partition is locked with share-update-exclusive
19939 * in the first transaction. This allows concurrent transactions to be
19940 * doing DML to the partition.
19942 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
19943 AccessExclusiveLock
);
19946 * Check inheritance conditions and either delete the pg_inherits row (in
19947 * non-concurrent mode) or just set the inhdetachpending flag.
19950 RemoveInheritance(partRel
, rel
, false);
19952 MarkInheritDetached(partRel
, rel
);
19955 * Ensure that foreign keys still hold after this detach. This keeps
19956 * locks on the referencing tables, which prevents concurrent transactions
19957 * from adding rows that we wouldn't see. For this to work in concurrent
19958 * mode, it is critical that the partition appears as no longer attached
19959 * for the RI queries as soon as the first transaction commits.
19961 ATDetachCheckNoForeignKeyRefs(partRel
);
19964 * Concurrent mode has to work harder; first we add a new constraint to
19965 * the partition that matches the partition constraint. Then we close our
19966 * existing transaction, and in a new one wait for all processes to catch
19967 * up on the catalog updates we've done so far; at that point we can
19968 * complete the operation.
19975 char *parentrelname
;
19979 * Add a new constraint to the partition being detached, which
19980 * supplants the partition constraint (unless there is one already).
19982 DetachAddConstraintIfNeeded(wqueue
, partRel
);
19985 * We're almost done now; the only traces that remain are the
19986 * pg_inherits tuple and the partition's relpartbounds. Before we can
19987 * remove those, we need to wait until all transactions that know that
19988 * this is a partition are gone.
19992 * Remember relation OIDs to re-acquire them later; and relation names
19993 * too, for error messages if something is dropped in between.
19995 partrelid
= RelationGetRelid(partRel
);
19996 parentrelid
= RelationGetRelid(rel
);
19997 parentrelname
= MemoryContextStrdup(PortalContext
,
19998 RelationGetRelationName(rel
));
19999 partrelname
= MemoryContextStrdup(PortalContext
,
20000 RelationGetRelationName(partRel
));
20002 /* Invalidate relcache entries for the parent -- must be before close */
20003 CacheInvalidateRelcache(rel
);
20005 table_close(partRel
, NoLock
);
20006 table_close(rel
, NoLock
);
20009 /* Make updated catalog entry visible */
20010 PopActiveSnapshot();
20011 CommitTransactionCommand();
20013 StartTransactionCommand();
20016 * Now wait. This ensures that all queries that were planned
20017 * including the partition are finished before we remove the rest of
20018 * catalog entries. We don't need or indeed want to acquire this
20019 * lock, though -- that would block later queries.
20021 * We don't need to concern ourselves with waiting for a lock on the
20022 * partition itself, since we will acquire AccessExclusiveLock below.
20024 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
20025 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
20028 * Now acquire locks in both relations again. Note they may have been
20029 * removed in the meantime, so care is required.
20031 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
20032 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
20034 /* If the relations aren't there, something bad happened; bail out */
20037 if (partRel
!= NULL
) /* shouldn't happen */
20038 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
20041 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20042 errmsg("partitioned table \"%s\" was removed concurrently",
20045 if (partRel
== NULL
)
20047 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20048 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
20053 /* Do the final part of detaching */
20054 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
20056 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
20058 /* keep our lock until commit */
20059 table_close(partRel
, NoLock
);
20065 * Second part of ALTER TABLE .. DETACH.
20067 * This is separate so that it can be run independently when the second
20068 * transaction of the concurrent algorithm fails (crash or abort).
20071 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
20072 Oid defaultPartOid
)
20078 Datum new_val
[Natts_pg_class
];
20079 bool new_null
[Natts_pg_class
],
20080 new_repl
[Natts_pg_class
];
20083 Relation trigrel
= NULL
;
20088 * We can remove the pg_inherits row now. (In the non-concurrent case,
20089 * this was already done).
20091 RemoveInheritance(partRel
, rel
, true);
20094 /* Drop any triggers that were cloned on creation/attach. */
20095 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
20098 * Detach any foreign keys that are inherited. This includes creating
20099 * additional action triggers.
20101 fks
= copyObject(RelationGetFKeyList(partRel
));
20103 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
20106 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
20108 Form_pg_constraint conform
;
20109 Constraint
*fkconstraint
;
20110 Oid insertTriggerOid
,
20113 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
20114 if (!HeapTupleIsValid(contup
))
20115 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
20116 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
20118 /* consider only the inherited foreign keys */
20119 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
20120 !OidIsValid(conform
->conparentid
))
20122 ReleaseSysCache(contup
);
20126 /* unset conparentid and adjust conislocal, coninhcount, etc. */
20127 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
20130 * Also, look up the partition's "check" triggers corresponding to the
20131 * constraint being detached and detach them from the parent triggers.
20133 GetForeignKeyCheckTriggers(trigrel
,
20134 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
20135 &insertTriggerOid
, &updateTriggerOid
);
20136 Assert(OidIsValid(insertTriggerOid
));
20137 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
20138 RelationGetRelid(partRel
));
20139 Assert(OidIsValid(updateTriggerOid
));
20140 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
20141 RelationGetRelid(partRel
));
20144 * Make the action triggers on the referenced relation. When this was
20145 * a partition the action triggers pointed to the parent rel (they
20146 * still do), but now we need separate ones of our own.
20148 fkconstraint
= makeNode(Constraint
);
20149 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
20150 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
20151 fkconstraint
->deferrable
= conform
->condeferrable
;
20152 fkconstraint
->initdeferred
= conform
->condeferred
;
20153 fkconstraint
->location
= -1;
20154 fkconstraint
->pktable
= NULL
;
20155 fkconstraint
->fk_attrs
= NIL
;
20156 fkconstraint
->pk_attrs
= NIL
;
20157 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
20158 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
20159 fkconstraint
->fk_del_action
= conform
->confdeltype
;
20160 fkconstraint
->fk_del_set_cols
= NIL
;
20161 fkconstraint
->old_conpfeqop
= NIL
;
20162 fkconstraint
->old_pktable_oid
= InvalidOid
;
20163 fkconstraint
->skip_validation
= false;
20164 fkconstraint
->initially_valid
= true;
20166 createForeignKeyActionTriggers(partRel
, conform
->confrelid
,
20167 fkconstraint
, fk
->conoid
,
20169 InvalidOid
, InvalidOid
,
20172 ReleaseSysCache(contup
);
20174 list_free_deep(fks
);
20176 table_close(trigrel
, RowExclusiveLock
);
20179 * Any sub-constraints that are in the referenced-side of a larger
20180 * constraint have to be removed. This partition is no longer part of the
20181 * key space of the constraint.
20183 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
20185 Oid constrOid
= lfirst_oid(cell
);
20186 ObjectAddress constraint
;
20188 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20189 deleteDependencyRecordsForClass(ConstraintRelationId
,
20191 ConstraintRelationId
,
20192 DEPENDENCY_INTERNAL
);
20193 CommandCounterIncrement();
20195 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
20196 performDeletion(&constraint
, DROP_RESTRICT
, 0);
20199 /* Now we can detach indexes */
20200 indexes
= RelationGetIndexList(partRel
);
20201 foreach(cell
, indexes
)
20203 Oid idxid
= lfirst_oid(cell
);
20207 if (!has_superclass(idxid
))
20210 Assert((IndexGetRelation(get_partition_parent(idxid
, false), false) ==
20211 RelationGetRelid(rel
)));
20213 idx
= index_open(idxid
, AccessExclusiveLock
);
20214 IndexSetParentIndex(idx
, InvalidOid
);
20216 /* If there's a constraint associated with the index, detach it too */
20217 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
20219 if (OidIsValid(constrOid
))
20220 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20222 index_close(idx
, NoLock
);
20225 /* Update pg_class tuple */
20226 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
20227 tuple
= SearchSysCacheCopy1(RELOID
,
20228 ObjectIdGetDatum(RelationGetRelid(partRel
)));
20229 if (!HeapTupleIsValid(tuple
))
20230 elog(ERROR
, "cache lookup failed for relation %u",
20231 RelationGetRelid(partRel
));
20232 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
20234 /* Clear relpartbound and reset relispartition */
20235 memset(new_val
, 0, sizeof(new_val
));
20236 memset(new_null
, false, sizeof(new_null
));
20237 memset(new_repl
, false, sizeof(new_repl
));
20238 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
20239 new_null
[Anum_pg_class_relpartbound
- 1] = true;
20240 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
20241 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
20242 new_val
, new_null
, new_repl
);
20244 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
20245 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
20246 heap_freetuple(newtuple
);
20247 table_close(classRel
, RowExclusiveLock
);
20250 * Drop identity property from all identity columns of partition.
20252 for (int attno
= 0; attno
< RelationGetNumberOfAttributes(partRel
); attno
++)
20254 Form_pg_attribute attr
= TupleDescAttr(partRel
->rd_att
, attno
);
20256 if (!attr
->attisdropped
&& attr
->attidentity
)
20257 ATExecDropIdentity(partRel
, NameStr(attr
->attname
), false,
20258 AccessExclusiveLock
, true, true);
20261 if (OidIsValid(defaultPartOid
))
20264 * If the relation being detached is the default partition itself,
20265 * remove it from the parent's pg_partitioned_table entry.
20267 * If not, we must invalidate default partition's relcache entry, as
20268 * in StorePartitionBound: its partition constraint depends on every
20269 * other partition's partition constraint.
20271 if (RelationGetRelid(partRel
) == defaultPartOid
)
20272 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
20274 CacheInvalidateRelcacheByRelid(defaultPartOid
);
20278 * Invalidate the parent's relcache so that the partition is no longer
20279 * included in its partition descriptor.
20281 CacheInvalidateRelcache(rel
);
20284 * If the partition we just detached is partitioned itself, invalidate
20285 * relcache for all descendent partitions too to ensure that their
20286 * rd_partcheck expression trees are rebuilt; must lock partitions before
20287 * doing so, using the same lockmode as what partRel has been locked with
20290 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
20294 children
= find_all_inheritors(RelationGetRelid(partRel
),
20295 AccessExclusiveLock
, NULL
);
20296 foreach(cell
, children
)
20298 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
20304 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20306 * To use when a DETACH PARTITION command previously did not run to
20307 * completion; this completes the detaching process.
20309 static ObjectAddress
20310 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
20313 ObjectAddress address
;
20314 Snapshot snap
= GetActiveSnapshot();
20316 partRel
= table_openrv(name
, AccessExclusiveLock
);
20319 * Wait until existing snapshots are gone. This is important if the
20320 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20321 * user could immediately run DETACH FINALIZE without actually waiting for
20322 * existing transactions. We must not complete the detach action until
20323 * all such queries are complete (otherwise we would present them with an
20324 * inconsistent view of catalogs).
20326 WaitForOlderSnapshots(snap
->xmin
, false);
20328 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
20330 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
20332 table_close(partRel
, NoLock
);
20338 * DetachAddConstraintIfNeeded
20339 * Subroutine for ATExecDetachPartition. Create a constraint that
20340 * takes the place of the partition constraint, but avoid creating
20341 * a dupe if a constraint already exists which implies the needed
20345 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
20347 List
*constraintExpr
;
20349 constraintExpr
= RelationGetPartitionQual(partRel
);
20350 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
20353 * Avoid adding a new constraint if the needed constraint is implied by an
20354 * existing constraint
20356 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
20358 AlteredTableInfo
*tab
;
20361 tab
= ATGetQueueEntry(wqueue
, partRel
);
20363 /* Add constraint on partition, equivalent to the partition constraint */
20364 n
= makeNode(Constraint
);
20365 n
->contype
= CONSTR_CHECK
;
20368 n
->is_no_inherit
= false;
20369 n
->raw_expr
= NULL
;
20370 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
20371 n
->initially_valid
= true;
20372 n
->skip_validation
= true;
20373 /* It's a re-add, since it nominally already exists */
20374 ATAddCheckNNConstraint(wqueue
, tab
, partRel
, n
,
20375 true, false, true, ShareUpdateExclusiveLock
);
20380 * DropClonedTriggersFromPartition
20381 * subroutine for ATExecDetachPartition to remove any triggers that were
20382 * cloned to the partition when it was created-as-partition or attached.
20383 * This undoes what CloneRowTriggersToPartition did.
20386 DropClonedTriggersFromPartition(Oid partitionId
)
20392 ObjectAddresses
*objects
;
20394 objects
= new_object_addresses();
20397 * Scan pg_trigger to search for all triggers on this rel.
20399 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
20400 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
20401 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
20402 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
20403 true, NULL
, 1, &skey
);
20404 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
20406 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
20407 ObjectAddress trig
;
20409 /* Ignore triggers that weren't cloned */
20410 if (!OidIsValid(pg_trigger
->tgparentid
))
20414 * Ignore internal triggers that are implementation objects of foreign
20415 * keys, because these will be detached when the foreign keys
20418 if (OidIsValid(pg_trigger
->tgconstrrelid
))
20422 * This is ugly, but necessary: remove the dependency markings on the
20423 * trigger so that it can be removed.
20425 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20427 DEPENDENCY_PARTITION_PRI
);
20428 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20429 RelationRelationId
,
20430 DEPENDENCY_PARTITION_SEC
);
20432 /* remember this trigger to remove it below */
20433 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
20434 add_exact_object_address(&trig
, objects
);
20437 /* make the dependency removal visible to the deletion below */
20438 CommandCounterIncrement();
20439 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
20442 free_object_addresses(objects
);
20443 systable_endscan(scan
);
20444 table_close(tgrel
, RowExclusiveLock
);
20448 * Before acquiring lock on an index, acquire the same lock on the owning
20451 struct AttachIndexCallbackState
20455 bool lockedParentTbl
;
20459 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
20462 struct AttachIndexCallbackState
*state
;
20463 Form_pg_class classform
;
20466 state
= (struct AttachIndexCallbackState
*) arg
;
20468 if (!state
->lockedParentTbl
)
20470 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
20471 state
->lockedParentTbl
= true;
20475 * If we previously locked some other heap, and the name we're looking up
20476 * no longer refers to an index on that relation, release the now-useless
20477 * lock. XXX maybe we should do *after* we verify whether the index does
20478 * not actually belong to the same relation ...
20480 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
20482 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
20483 state
->partitionOid
= InvalidOid
;
20486 /* Didn't find a relation, so no need for locking or permission checks. */
20487 if (!OidIsValid(relOid
))
20490 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
20491 if (!HeapTupleIsValid(tuple
))
20492 return; /* concurrently dropped, so nothing to do */
20493 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
20494 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
20495 classform
->relkind
!= RELKIND_INDEX
)
20497 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20498 errmsg("\"%s\" is not an index", rv
->relname
)));
20499 ReleaseSysCache(tuple
);
20502 * Since we need only examine the heap's tupledesc, an access share lock
20503 * on it (preventing any DDL) is sufficient.
20505 state
->partitionOid
= IndexGetRelation(relOid
, false);
20506 LockRelationOid(state
->partitionOid
, AccessShareLock
);
20510 * ALTER INDEX i1 ATTACH PARTITION i2
20512 static ObjectAddress
20513 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
20517 Relation parentTbl
;
20518 ObjectAddress address
;
20521 struct AttachIndexCallbackState state
;
20524 * We need to obtain lock on the index 'name' to modify it, but we also
20525 * need to read its owning table's tuple descriptor -- so we need to lock
20526 * both. To avoid deadlocks, obtain lock on the table before doing so on
20527 * the index. Furthermore, we need to examine the parent table of the
20528 * partition, so lock that one too.
20530 state
.partitionOid
= InvalidOid
;
20531 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
20532 state
.lockedParentTbl
= false;
20534 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
20535 RangeVarCallbackForAttachIndex
,
20538 if (!OidIsValid(partIdxId
))
20540 (errcode(ERRCODE_UNDEFINED_OBJECT
),
20541 errmsg("index \"%s\" does not exist", name
->relname
)));
20543 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20544 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
20546 /* we already hold locks on both tables, so this is safe: */
20547 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
20548 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
20550 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
20552 /* Silently do nothing if already in the right state */
20553 currParent
= partIdx
->rd_rel
->relispartition
?
20554 get_partition_parent(partIdxId
, false) : InvalidOid
;
20555 if (currParent
!= RelationGetRelid(parentIdx
))
20557 IndexInfo
*childInfo
;
20558 IndexInfo
*parentInfo
;
20562 PartitionDesc partDesc
;
20564 cldConstrId
= InvalidOid
;
20567 * If this partition already has an index attached, refuse the
20570 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
20572 if (OidIsValid(currParent
))
20574 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20575 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20576 RelationGetRelationName(partIdx
),
20577 RelationGetRelationName(parentIdx
)),
20578 errdetail("Index \"%s\" is already attached to another index.",
20579 RelationGetRelationName(partIdx
))));
20581 /* Make sure it indexes a partition of the other index's table */
20582 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
20584 for (i
= 0; i
< partDesc
->nparts
; i
++)
20586 if (partDesc
->oids
[i
] == state
.partitionOid
)
20594 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20595 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20596 RelationGetRelationName(partIdx
),
20597 RelationGetRelationName(parentIdx
)),
20598 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20599 RelationGetRelationName(partIdx
),
20600 RelationGetRelationName(parentTbl
))));
20602 /* Ensure the indexes are compatible */
20603 childInfo
= BuildIndexInfo(partIdx
);
20604 parentInfo
= BuildIndexInfo(parentIdx
);
20605 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
20606 RelationGetDescr(parentTbl
),
20608 if (!CompareIndexInfo(childInfo
, parentInfo
,
20609 partIdx
->rd_indcollation
,
20610 parentIdx
->rd_indcollation
,
20611 partIdx
->rd_opfamily
,
20612 parentIdx
->rd_opfamily
,
20615 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20616 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20617 RelationGetRelationName(partIdx
),
20618 RelationGetRelationName(parentIdx
)),
20619 errdetail("The index definitions do not match.")));
20622 * If there is a constraint in the parent, make sure there is one in
20625 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
20626 RelationGetRelid(parentIdx
));
20628 if (OidIsValid(constraintOid
))
20630 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
20632 if (!OidIsValid(cldConstrId
))
20634 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20635 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20636 RelationGetRelationName(partIdx
),
20637 RelationGetRelationName(parentIdx
)),
20638 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20639 RelationGetRelationName(parentIdx
),
20640 RelationGetRelationName(parentTbl
),
20641 RelationGetRelationName(partIdx
))));
20645 * If it's a primary key, make sure the columns in the partition are
20648 if (parentIdx
->rd_index
->indisprimary
)
20649 verifyPartitionIndexNotNull(childInfo
, partTbl
);
20651 /* All good -- do it */
20652 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
20653 if (OidIsValid(constraintOid
))
20654 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
20655 RelationGetRelid(partTbl
));
20657 free_attrmap(attmap
);
20659 validatePartitionedIndex(parentIdx
, parentTbl
);
20662 relation_close(parentTbl
, AccessShareLock
);
20663 /* keep these locks till commit */
20664 relation_close(partTbl
, NoLock
);
20665 relation_close(partIdx
, NoLock
);
20671 * Verify whether the given partition already contains an index attached
20672 * to the given partitioned index. If so, raise an error.
20675 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
20679 existingIdx
= index_get_partition(partitionTbl
,
20680 RelationGetRelid(parentIdx
));
20681 if (OidIsValid(existingIdx
))
20683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20685 RelationGetRelationName(partIdx
),
20686 RelationGetRelationName(parentIdx
)),
20687 errdetail("Another index is already attached for partition \"%s\".",
20688 RelationGetRelationName(partitionTbl
))));
20692 * Verify whether the set of attached partition indexes to a parent index on
20693 * a partitioned table is complete. If it is, mark the parent index valid.
20695 * This should be called each time a partition index is attached.
20698 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
20700 Relation inheritsRel
;
20705 bool updated
= false;
20707 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
20710 * Scan pg_inherits for this parent index. Count each valid index we find
20711 * (verifying the pg_index entry for each), and if we reach the total
20712 * amount we expect, we can mark this parent index as valid.
20714 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
20715 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
20716 BTEqualStrategyNumber
, F_OIDEQ
,
20717 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20718 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
20720 while ((inhTup
= systable_getnext(scan
)) != NULL
)
20722 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
20724 Form_pg_index indexForm
;
20726 indTup
= SearchSysCache1(INDEXRELID
,
20727 ObjectIdGetDatum(inhForm
->inhrelid
));
20728 if (!HeapTupleIsValid(indTup
))
20729 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
20730 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20731 if (indexForm
->indisvalid
)
20733 ReleaseSysCache(indTup
);
20736 /* Done with pg_inherits */
20737 systable_endscan(scan
);
20738 table_close(inheritsRel
, AccessShareLock
);
20741 * If we found as many inherited indexes as the partitioned table has
20742 * partitions, we're good; update pg_index to set indisvalid.
20744 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
20748 Form_pg_index indexForm
;
20750 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
20751 indTup
= SearchSysCacheCopy1(INDEXRELID
,
20752 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20753 if (!HeapTupleIsValid(indTup
))
20754 elog(ERROR
, "cache lookup failed for index %u",
20755 RelationGetRelid(partedIdx
));
20756 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20758 indexForm
->indisvalid
= true;
20761 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
20763 table_close(idxRel
, RowExclusiveLock
);
20764 heap_freetuple(indTup
);
20768 * If this index is in turn a partition of a larger index, validating it
20769 * might cause the parent to become valid also. Try that.
20771 if (updated
&& partedIdx
->rd_rel
->relispartition
)
20775 Relation parentIdx
,
20778 /* make sure we see the validation we just did */
20779 CommandCounterIncrement();
20781 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
20782 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
20783 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
20784 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
20785 Assert(!parentIdx
->rd_index
->indisvalid
);
20787 validatePartitionedIndex(parentIdx
, parentTbl
);
20789 relation_close(parentIdx
, AccessExclusiveLock
);
20790 relation_close(parentTbl
, AccessExclusiveLock
);
20795 * When attaching an index as a partition of a partitioned index which is a
20796 * primary key, verify that all the columns in the partition are marked NOT
20800 verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partition
)
20802 for (int i
= 0; i
< iinfo
->ii_NumIndexKeyAttrs
; i
++)
20804 Form_pg_attribute att
= TupleDescAttr(RelationGetDescr(partition
),
20805 iinfo
->ii_IndexAttrNumbers
[i
] - 1);
20807 if (!att
->attnotnull
)
20809 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
20810 errmsg("invalid primary key definition"),
20811 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20812 NameStr(att
->attname
),
20813 RelationGetRelationName(partition
)));
20818 * Return an OID list of constraints that reference the given relation
20819 * that are marked as having a parent constraints.
20822 GetParentedForeignKeyRefs(Relation partition
)
20824 Relation pg_constraint
;
20827 ScanKeyData key
[2];
20828 List
*constraints
= NIL
;
20831 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20834 if (RelationGetIndexList(partition
) == NIL
||
20835 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
20836 INDEX_ATTR_BITMAP_KEY
)))
20839 /* Search for constraints referencing this table */
20840 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
20841 ScanKeyInit(&key
[0],
20842 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
20843 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
20844 ScanKeyInit(&key
[1],
20845 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
20846 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
20848 /* XXX This is a seqscan, as we don't have a usable index */
20849 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
20850 while ((tuple
= systable_getnext(scan
)) != NULL
)
20852 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20855 * We only need to process constraints that are part of larger ones.
20857 if (!OidIsValid(constrForm
->conparentid
))
20860 constraints
= lappend_oid(constraints
, constrForm
->oid
);
20863 systable_endscan(scan
);
20864 table_close(pg_constraint
, AccessShareLock
);
20866 return constraints
;
20870 * During DETACH PARTITION, verify that any foreign keys pointing to the
20871 * partitioned table would not become invalid. An error is raised if any
20872 * referenced values exist.
20875 ATDetachCheckNoForeignKeyRefs(Relation partition
)
20880 constraints
= GetParentedForeignKeyRefs(partition
);
20882 foreach(cell
, constraints
)
20884 Oid constrOid
= lfirst_oid(cell
);
20886 Form_pg_constraint constrForm
;
20888 Trigger trig
= {0};
20890 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
20891 if (!HeapTupleIsValid(tuple
))
20892 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
20893 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20895 Assert(OidIsValid(constrForm
->conparentid
));
20896 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
20898 /* prevent data changes into the referencing table until commit */
20899 rel
= table_open(constrForm
->conrelid
, ShareLock
);
20901 trig
.tgoid
= InvalidOid
;
20902 trig
.tgname
= NameStr(constrForm
->conname
);
20903 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
20904 trig
.tgisinternal
= true;
20905 trig
.tgconstrrelid
= RelationGetRelid(partition
);
20906 trig
.tgconstrindid
= constrForm
->conindid
;
20907 trig
.tgconstraint
= constrForm
->oid
;
20908 trig
.tgdeferrable
= false;
20909 trig
.tginitdeferred
= false;
20910 /* we needn't fill in remaining fields */
20912 RI_PartitionRemove_Check(&trig
, rel
, partition
);
20914 ReleaseSysCache(tuple
);
20916 table_close(rel
, NoLock
);
20921 * resolve column compression specification to compression method.
20924 GetAttributeCompression(Oid atttypid
, const char *compression
)
20928 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
20929 return InvalidCompressionMethod
;
20932 * To specify a nondefault method, the column data type must be toastable.
20933 * Note this says nothing about whether the column's attstorage setting
20934 * permits compression; we intentionally allow attstorage and
20935 * attcompression to be independent. But with a non-toastable type,
20936 * attstorage could not be set to a value that would permit compression.
20938 * We don't actually need to enforce this, since nothing bad would happen
20939 * if attcompression were non-default; it would never be consulted. But
20940 * it seems more user-friendly to complain about a certainly-useless
20941 * attempt to set the property.
20943 if (!TypeIsToastable(atttypid
))
20945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20946 errmsg("column data type %s does not support compression",
20947 format_type_be(atttypid
))));
20949 cmethod
= CompressionNameToMethod(compression
);
20950 if (!CompressionMethodIsValid(cmethod
))
20952 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20953 errmsg("invalid compression method \"%s\"", compression
)));
20959 * resolve column storage specification
20962 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
20966 if (pg_strcasecmp(storagemode
, "plain") == 0)
20967 cstorage
= TYPSTORAGE_PLAIN
;
20968 else if (pg_strcasecmp(storagemode
, "external") == 0)
20969 cstorage
= TYPSTORAGE_EXTERNAL
;
20970 else if (pg_strcasecmp(storagemode
, "extended") == 0)
20971 cstorage
= TYPSTORAGE_EXTENDED
;
20972 else if (pg_strcasecmp(storagemode
, "main") == 0)
20973 cstorage
= TYPSTORAGE_MAIN
;
20974 else if (pg_strcasecmp(storagemode
, "default") == 0)
20975 cstorage
= get_typstorage(atttypid
);
20978 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20979 errmsg("invalid storage type \"%s\"",
20983 * safety check: do not allow toasted storage modes unless column datatype
20986 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
20988 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20989 errmsg("column data type %s can only have storage PLAIN",
20990 format_type_be(atttypid
))));
20996 * Struct with context of new partition for inserting rows from split partition
20998 typedef struct SplitPartitionContext
21000 ExprState
*partqualstate
; /* expression for checking slot for partition
21001 * (NULL for DEFAULT partition) */
21002 BulkInsertState bistate
; /* state of bulk inserts for partition */
21003 TupleTableSlot
*dstslot
; /* slot for inserting row into partition */
21004 Relation partRel
; /* relation for partition */
21005 } SplitPartitionContext
;
21009 * createSplitPartitionContext: create context for partition and fill it
21011 static SplitPartitionContext
*
21012 createSplitPartitionContext(Relation partRel
)
21014 SplitPartitionContext
*pc
;
21016 pc
= (SplitPartitionContext
*) palloc0(sizeof(SplitPartitionContext
));
21017 pc
->partRel
= partRel
;
21020 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
21021 * don't bother using it.
21023 pc
->bistate
= GetBulkInsertState();
21025 /* Create tuple slot for new partition. */
21026 pc
->dstslot
= MakeSingleTupleTableSlot(RelationGetDescr(pc
->partRel
),
21027 table_slot_callbacks(pc
->partRel
));
21028 ExecStoreAllNullTuple(pc
->dstslot
);
21034 * deleteSplitPartitionContext: delete context for partition
21037 deleteSplitPartitionContext(SplitPartitionContext
*pc
, int ti_options
)
21039 ExecDropSingleTupleTableSlot(pc
->dstslot
);
21040 FreeBulkInsertState(pc
->bistate
);
21042 table_finish_bulk_insert(pc
->partRel
, ti_options
);
21048 * moveSplitTableRows: scan split partition (splitRel) of partitioned table
21049 * (rel) and move rows into new partitions.
21051 * New partitions description:
21052 * partlist: list of pointers to SinglePartitionSpec structures.
21053 * newPartRels: list of Relations.
21054 * defaultPartOid: oid of DEFAULT partition, for table rel.
21057 moveSplitTableRows(Relation rel
, Relation splitRel
, List
*partlist
, List
*newPartRels
, Oid defaultPartOid
)
21059 /* The FSM is empty, so don't bother using it. */
21060 int ti_options
= TABLE_INSERT_SKIP_FSM
;
21065 TupleTableSlot
*srcslot
;
21066 ExprContext
*econtext
;
21067 TableScanDesc scan
;
21069 MemoryContext oldCxt
;
21070 List
*partContexts
= NIL
;
21071 TupleConversionMap
*tuple_map
;
21072 SplitPartitionContext
*defaultPartCtx
= NULL
,
21074 bool isOldDefaultPart
= false;
21076 mycid
= GetCurrentCommandId(true);
21078 estate
= CreateExecutorState();
21080 forboth(listptr
, partlist
, listptr2
, newPartRels
)
21082 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
21084 pc
= createSplitPartitionContext((Relation
) lfirst(listptr2
));
21086 if (sps
->bound
->is_default
)
21088 /* We should not create constraint for detached DEFAULT partition. */
21089 defaultPartCtx
= pc
;
21093 List
*partConstraint
;
21095 /* Build expression execution states for partition check quals. */
21096 partConstraint
= get_qual_from_partbound(rel
, sps
->bound
);
21098 (List
*) eval_const_expressions(NULL
,
21099 (Node
*) partConstraint
);
21100 /* Make boolean expression for ExecCheck(). */
21101 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
21104 * Map the vars in the constraint expression from rel's attnos to
21107 partConstraint
= map_partition_varattnos(partConstraint
,
21110 pc
->partqualstate
=
21111 ExecPrepareExpr((Expr
*) linitial(partConstraint
), estate
);
21112 Assert(pc
->partqualstate
!= NULL
);
21115 /* Store partition context into list. */
21116 partContexts
= lappend(partContexts
, pc
);
21120 * Create partition context for DEFAULT partition. We can insert values
21121 * into this partition in case spaces with values between new partitions.
21123 if (!defaultPartCtx
&& OidIsValid(defaultPartOid
))
21125 /* Indicate that we allocate context for old DEFAULT partition */
21126 isOldDefaultPart
= true;
21127 defaultPartCtx
= createSplitPartitionContext(table_open(defaultPartOid
, AccessExclusiveLock
));
21130 econtext
= GetPerTupleExprContext(estate
);
21132 /* Create necessary tuple slot. */
21133 srcslot
= MakeSingleTupleTableSlot(RelationGetDescr(splitRel
),
21134 table_slot_callbacks(splitRel
));
21137 * Map computing for moving attributes of split partition to new partition
21138 * (for first new partition, but other new partitions can use the same
21141 pc
= (SplitPartitionContext
*) lfirst(list_head(partContexts
));
21142 tuple_map
= convert_tuples_by_name(RelationGetDescr(splitRel
),
21143 RelationGetDescr(pc
->partRel
));
21145 /* Scan through the rows. */
21146 snapshot
= RegisterSnapshot(GetLatestSnapshot());
21147 scan
= table_beginscan(splitRel
, snapshot
, 0, NULL
);
21150 * Switch to per-tuple memory context and reset it for each tuple
21151 * produced, so we don't leak memory.
21153 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
21155 while (table_scan_getnextslot(scan
, ForwardScanDirection
, srcslot
))
21157 bool found
= false;
21158 TupleTableSlot
*insertslot
;
21160 /* Extract data from old tuple. */
21161 slot_getallattrs(srcslot
);
21163 econtext
->ecxt_scantuple
= srcslot
;
21165 /* Search partition for current slot srcslot. */
21166 foreach(listptr
, partContexts
)
21168 pc
= (SplitPartitionContext
*) lfirst(listptr
);
21170 if (pc
->partqualstate
/* skip DEFAULT partition */ &&
21171 ExecCheck(pc
->partqualstate
, econtext
))
21176 ResetExprContext(econtext
);
21180 /* Use DEFAULT partition if it exists. */
21181 if (defaultPartCtx
)
21182 pc
= defaultPartCtx
;
21185 (errcode(ERRCODE_CHECK_VIOLATION
),
21186 errmsg("can not find partition for split partition row"),
21187 errtable(splitRel
)));
21192 /* Need to use map to copy attributes. */
21193 insertslot
= execute_attr_map_slot(tuple_map
->attrMap
, srcslot
, pc
->dstslot
);
21197 /* Copy attributes directly. */
21198 insertslot
= pc
->dstslot
;
21200 ExecClearTuple(insertslot
);
21202 memcpy(insertslot
->tts_values
, srcslot
->tts_values
,
21203 sizeof(Datum
) * srcslot
->tts_nvalid
);
21204 memcpy(insertslot
->tts_isnull
, srcslot
->tts_isnull
,
21205 sizeof(bool) * srcslot
->tts_nvalid
);
21207 ExecStoreVirtualTuple(insertslot
);
21210 /* Write the tuple out to the new relation. */
21211 table_tuple_insert(pc
->partRel
, insertslot
, mycid
,
21212 ti_options
, pc
->bistate
);
21214 ResetExprContext(econtext
);
21216 CHECK_FOR_INTERRUPTS();
21219 MemoryContextSwitchTo(oldCxt
);
21221 table_endscan(scan
);
21222 UnregisterSnapshot(snapshot
);
21225 free_conversion_map(tuple_map
);
21227 ExecDropSingleTupleTableSlot(srcslot
);
21229 FreeExecutorState(estate
);
21231 foreach(listptr
, partContexts
)
21232 deleteSplitPartitionContext((SplitPartitionContext
*) lfirst(listptr
), ti_options
);
21234 /* Need to close table and free buffers for DEFAULT partition. */
21235 if (isOldDefaultPart
)
21237 Relation defaultPartRel
= defaultPartCtx
->partRel
;
21239 deleteSplitPartitionContext(defaultPartCtx
, ti_options
);
21240 /* Keep the lock until commit. */
21241 table_close(defaultPartRel
, NoLock
);
21246 * createPartitionTable: create table for a new partition with given name
21247 * (newPartName) like table (modelRel)
21249 * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
21250 * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
21252 * Also, this function sets the new partition access method same as parent
21253 * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
21254 * checks that parent and child tables have compatible persistence.
21256 * Function returns the created relation (locked in AccessExclusiveLock mode).
21259 createPartitionTable(RangeVar
*newPartName
, Relation modelRel
,
21260 AlterTableUtilityContext
*context
)
21262 CreateStmt
*createStmt
;
21263 TableLikeClause
*tlc
;
21264 PlannedStmt
*wrapper
;
21267 /* If existing rel is temp, it must belong to this session */
21268 if (modelRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
21269 !modelRel
->rd_islocaltemp
)
21271 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
21272 errmsg("cannot create as partition of temporary relation of another session")));
21274 /* New partition should have the same persistence as modelRel */
21275 newPartName
->relpersistence
= modelRel
->rd_rel
->relpersistence
;
21277 createStmt
= makeNode(CreateStmt
);
21278 createStmt
->relation
= newPartName
;
21279 createStmt
->tableElts
= NIL
;
21280 createStmt
->inhRelations
= NIL
;
21281 createStmt
->constraints
= NIL
;
21282 createStmt
->options
= NIL
;
21283 createStmt
->oncommit
= ONCOMMIT_NOOP
;
21284 createStmt
->tablespacename
= NULL
;
21285 createStmt
->if_not_exists
= false;
21286 createStmt
->accessMethod
= get_am_name(modelRel
->rd_rel
->relam
);
21288 tlc
= makeNode(TableLikeClause
);
21289 tlc
->relation
= makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel
)),
21290 RelationGetRelationName(modelRel
), -1);
21293 * Indexes will be inherited on "attach new partitions" stage, after data
21296 tlc
->options
= CREATE_TABLE_LIKE_ALL
& ~(CREATE_TABLE_LIKE_INDEXES
| CREATE_TABLE_LIKE_IDENTITY
);
21297 tlc
->relationOid
= InvalidOid
;
21298 createStmt
->tableElts
= lappend(createStmt
->tableElts
, tlc
);
21300 /* Need to make a wrapper PlannedStmt. */
21301 wrapper
= makeNode(PlannedStmt
);
21302 wrapper
->commandType
= CMD_UTILITY
;
21303 wrapper
->canSetTag
= false;
21304 wrapper
->utilityStmt
= (Node
*) createStmt
;
21305 wrapper
->stmt_location
= context
->pstmt
->stmt_location
;
21306 wrapper
->stmt_len
= context
->pstmt
->stmt_len
;
21308 ProcessUtility(wrapper
,
21309 context
->queryString
,
21311 PROCESS_UTILITY_SUBCOMMAND
,
21318 * Open the new partition with no lock, because we already have
21319 * AccessExclusiveLock placed there after creation.
21321 newRel
= table_openrv(newPartName
, NoLock
);
21324 * We intended to create the partition with the same persistence as the
21325 * parent table, but we still need to recheck because that might be
21326 * affected by the search_path. If the parent is permanent, so must be
21327 * all of its partitions.
21329 if (modelRel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
21330 newRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
21332 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
21333 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
21334 RelationGetRelationName(modelRel
))));
21336 /* Permanent rels cannot be partitions belonging to temporary parent */
21337 if (newRel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
21338 modelRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
21340 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
21341 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
21342 RelationGetRelationName(modelRel
))));
21348 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
21351 ATExecSplitPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
21352 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
)
21356 char relname
[NAMEDATALEN
];
21360 bool isSameName
= false;
21361 char tmpRelName
[NAMEDATALEN
];
21362 List
*newPartRels
= NIL
;
21363 ObjectAddress object
;
21364 Oid defaultPartOid
;
21366 defaultPartOid
= get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
21369 * We are going to detach and remove this partition: need to use exclusive
21370 * lock for preventing DML-queries to the partition.
21372 splitRel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
21374 splitRelOid
= RelationGetRelid(splitRel
);
21376 /* Check descriptions of new partitions. */
21377 foreach(listptr
, cmd
->partlist
)
21379 Oid existing_relid
;
21380 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
21382 strlcpy(relname
, sps
->name
->relname
, NAMEDATALEN
);
21385 * Look up the namespace in which we are supposed to create the
21386 * partition, check we have permission to create there, lock it
21387 * against concurrent drop, and mark stmt->relation as
21388 * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21391 RangeVarGetAndCheckCreationNamespace(sps
->name
, NoLock
, NULL
);
21394 * This would fail later on anyway if the relation already exists. But
21395 * by catching it here we can emit a nicer error message.
21397 existing_relid
= get_relname_relid(relname
, namespaceId
);
21398 if (existing_relid
== splitRelOid
&& !isSameName
)
21399 /* One new partition can have the same name as split partition. */
21401 else if (existing_relid
!= InvalidOid
)
21403 (errcode(ERRCODE_DUPLICATE_TABLE
),
21404 errmsg("relation \"%s\" already exists", relname
)));
21407 /* Detach split partition. */
21408 RemoveInheritance(splitRel
, rel
, false);
21409 /* Do the final part of detaching. */
21410 DetachPartitionFinalize(rel
, splitRel
, false, defaultPartOid
);
21413 * If new partition has the same name as split partition then we should
21414 * rename split partition for reusing name.
21419 * We must bump the command counter to make the split partition tuple
21420 * visible for renaming.
21422 CommandCounterIncrement();
21423 /* Rename partition. */
21424 sprintf(tmpRelName
, "split-%u-%X-tmp", RelationGetRelid(rel
), MyProcPid
);
21425 RenameRelationInternal(splitRelOid
, tmpRelName
, false, false);
21428 * We must bump the command counter to make the split partition tuple
21429 * visible after renaming.
21431 CommandCounterIncrement();
21434 /* Create new partitions (like split partition), without indexes. */
21435 foreach(listptr
, cmd
->partlist
)
21437 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
21438 Relation newPartRel
;
21440 newPartRel
= createPartitionTable(sps
->name
, rel
, context
);
21441 newPartRels
= lappend(newPartRels
, newPartRel
);
21444 /* Copy data from split partition to new partitions. */
21445 moveSplitTableRows(rel
, splitRel
, cmd
->partlist
, newPartRels
, defaultPartOid
);
21446 /* Keep the lock until commit. */
21447 table_close(splitRel
, NoLock
);
21449 /* Attach new partitions to partitioned table. */
21450 forboth(listptr
, cmd
->partlist
, listptr2
, newPartRels
)
21452 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
21453 Relation newPartRel
= (Relation
) lfirst(listptr2
);
21456 * wqueue = NULL: verification for each cloned constraint is not
21459 attachPartitionTable(NULL
, rel
, newPartRel
, sps
->bound
);
21460 /* Keep the lock until commit. */
21461 table_close(newPartRel
, NoLock
);
21464 /* Drop split partition. */
21465 object
.classId
= RelationRelationId
;
21466 object
.objectId
= splitRelOid
;
21467 object
.objectSubId
= 0;
21468 /* Probably DROP_CASCADE is not needed. */
21469 performDeletion(&object
, DROP_RESTRICT
, 0);
21473 * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
21474 * of the partitioned table (rel) and move rows into the new partition
21478 moveMergedTablesRows(Relation rel
, List
*mergingPartitionsList
,
21479 Relation newPartRel
)
21483 /* The FSM is empty, so don't bother using it. */
21484 int ti_options
= TABLE_INSERT_SKIP_FSM
;
21486 BulkInsertState bistate
; /* state of bulk inserts for partition */
21487 TupleTableSlot
*dstslot
;
21489 mycid
= GetCurrentCommandId(true);
21491 /* Prepare a BulkInsertState for table_tuple_insert. */
21492 bistate
= GetBulkInsertState();
21494 /* Create necessary tuple slot. */
21495 dstslot
= MakeSingleTupleTableSlot(RelationGetDescr(newPartRel
),
21496 table_slot_callbacks(newPartRel
));
21497 ExecStoreAllNullTuple(dstslot
);
21499 foreach(listptr
, mergingPartitionsList
)
21501 Relation mergingPartition
= (Relation
) lfirst(listptr
);
21502 TupleTableSlot
*srcslot
;
21503 TupleConversionMap
*tuple_map
;
21504 TableScanDesc scan
;
21507 /* Create tuple slot for new partition. */
21508 srcslot
= MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition
),
21509 table_slot_callbacks(mergingPartition
));
21512 * Map computing for moving attributes of merged partition to new
21515 tuple_map
= convert_tuples_by_name(RelationGetDescr(mergingPartition
),
21516 RelationGetDescr(newPartRel
));
21518 /* Scan through the rows. */
21519 snapshot
= RegisterSnapshot(GetLatestSnapshot());
21520 scan
= table_beginscan(mergingPartition
, snapshot
, 0, NULL
);
21522 while (table_scan_getnextslot(scan
, ForwardScanDirection
, srcslot
))
21524 TupleTableSlot
*insertslot
;
21526 /* Extract data from old tuple. */
21527 slot_getallattrs(srcslot
);
21531 /* Need to use map to copy attributes. */
21532 insertslot
= execute_attr_map_slot(tuple_map
->attrMap
, srcslot
, dstslot
);
21536 /* Copy attributes directly. */
21537 insertslot
= dstslot
;
21539 ExecClearTuple(insertslot
);
21541 memcpy(insertslot
->tts_values
, srcslot
->tts_values
,
21542 sizeof(Datum
) * srcslot
->tts_nvalid
);
21543 memcpy(insertslot
->tts_isnull
, srcslot
->tts_isnull
,
21544 sizeof(bool) * srcslot
->tts_nvalid
);
21546 ExecStoreVirtualTuple(insertslot
);
21549 /* Write the tuple out to the new relation. */
21550 table_tuple_insert(newPartRel
, insertslot
, mycid
,
21551 ti_options
, bistate
);
21553 CHECK_FOR_INTERRUPTS();
21556 table_endscan(scan
);
21557 UnregisterSnapshot(snapshot
);
21560 free_conversion_map(tuple_map
);
21562 ExecDropSingleTupleTableSlot(srcslot
);
21565 ExecDropSingleTupleTableSlot(dstslot
);
21566 FreeBulkInsertState(bistate
);
21568 table_finish_bulk_insert(newPartRel
, ti_options
);
21572 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
21575 ATExecMergePartitions(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
21576 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
)
21578 Relation newPartRel
;
21580 List
*mergingPartitionsList
= NIL
;
21581 Oid defaultPartOid
;
21584 * Lock all merged partitions, check them and create list with partitions
21587 foreach(listptr
, cmd
->partlist
)
21589 RangeVar
*name
= (RangeVar
*) lfirst(listptr
);
21590 Relation mergingPartition
;
21593 * We are going to detach and remove this partition: need to use
21594 * exclusive lock for preventing DML-queries to the partition.
21596 mergingPartition
= table_openrv(name
, AccessExclusiveLock
);
21599 * Checking that two partitions have the same name was before, in
21600 * function transformPartitionCmdForMerge().
21602 if (equal(name
, cmd
->name
))
21604 /* One new partition can have the same name as merged partition. */
21605 char tmpRelName
[NAMEDATALEN
];
21607 /* Generate temporary name. */
21608 sprintf(tmpRelName
, "merge-%u-%X-tmp", RelationGetRelid(rel
), MyProcPid
);
21611 * Rename the existing partition with a temporary name, leaving it
21612 * free for the new partition. We don't need to care about this
21613 * in the future because we're going to eventually drop the
21614 * existing partition anyway.
21616 RenameRelationInternal(RelationGetRelid(mergingPartition
),
21617 tmpRelName
, false, false);
21620 * We must bump the command counter to make the new partition
21621 * tuple visible for rename.
21623 CommandCounterIncrement();
21626 /* Store a next merging partition into the list. */
21627 mergingPartitionsList
= lappend(mergingPartitionsList
,
21631 /* Detach all merged partitions. */
21633 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
21634 foreach(listptr
, mergingPartitionsList
)
21636 Relation mergingPartition
= (Relation
) lfirst(listptr
);
21638 /* Remove the pg_inherits row first. */
21639 RemoveInheritance(mergingPartition
, rel
, false);
21640 /* Do the final part of detaching. */
21641 DetachPartitionFinalize(rel
, mergingPartition
, false, defaultPartOid
);
21644 /* Create table for new partition, use partitioned table as model. */
21645 newPartRel
= createPartitionTable(cmd
->name
, rel
, context
);
21647 /* Copy data from merged partitions to new partition. */
21648 moveMergedTablesRows(rel
, mergingPartitionsList
, newPartRel
);
21650 /* Drop the current partitions before attaching the new one. */
21651 foreach(listptr
, mergingPartitionsList
)
21653 ObjectAddress object
;
21654 Relation mergingPartition
= (Relation
) lfirst(listptr
);
21656 /* Get relation id before table_close() call. */
21657 object
.objectId
= RelationGetRelid(mergingPartition
);
21658 object
.classId
= RelationRelationId
;
21659 object
.objectSubId
= 0;
21661 /* Keep the lock until commit. */
21662 table_close(mergingPartition
, NoLock
);
21664 performDeletion(&object
, DROP_RESTRICT
, 0);
21666 list_free(mergingPartitionsList
);
21669 * Attach a new partition to the partitioned table. wqueue = NULL:
21670 * verification for each cloned constraint is not needed.
21672 attachPartitionTable(NULL
, rel
, newPartRel
, cmd
->bound
);
21674 /* Keep the lock until commit. */
21675 table_close(newPartRel
, NoLock
);