1 /*-------------------------------------------------------------------------
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/heapam.h"
20 #include "access/heapam_xlog.h"
21 #include "access/multixact.h"
22 #include "access/reloptions.h"
23 #include "access/relscan.h"
24 #include "access/sysattr.h"
25 #include "access/tableam.h"
26 #include "access/toast_compression.h"
27 #include "access/xact.h"
28 #include "access/xlog.h"
29 #include "access/xloginsert.h"
30 #include "catalog/catalog.h"
31 #include "catalog/heap.h"
32 #include "catalog/index.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_attrdef.h"
38 #include "catalog/pg_collation.h"
39 #include "catalog/pg_constraint.h"
40 #include "catalog/pg_depend.h"
41 #include "catalog/pg_foreign_table.h"
42 #include "catalog/pg_inherits.h"
43 #include "catalog/pg_largeobject.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_opclass.h"
46 #include "catalog/pg_statistic_ext.h"
47 #include "catalog/pg_tablespace.h"
48 #include "catalog/pg_trigger.h"
49 #include "catalog/pg_type.h"
50 #include "catalog/storage.h"
51 #include "catalog/storage_xlog.h"
52 #include "catalog/toasting.h"
53 #include "commands/cluster.h"
54 #include "commands/comment.h"
55 #include "commands/defrem.h"
56 #include "commands/event_trigger.h"
57 #include "commands/policy.h"
58 #include "commands/sequence.h"
59 #include "commands/tablecmds.h"
60 #include "commands/tablespace.h"
61 #include "commands/trigger.h"
62 #include "commands/typecmds.h"
63 #include "commands/user.h"
64 #include "commands/vacuum.h"
65 #include "executor/executor.h"
66 #include "foreign/fdwapi.h"
67 #include "foreign/foreign.h"
68 #include "miscadmin.h"
69 #include "nodes/makefuncs.h"
70 #include "nodes/nodeFuncs.h"
71 #include "nodes/parsenodes.h"
72 #include "optimizer/optimizer.h"
73 #include "parser/parse_clause.h"
74 #include "parser/parse_coerce.h"
75 #include "parser/parse_collate.h"
76 #include "parser/parse_expr.h"
77 #include "parser/parse_oper.h"
78 #include "parser/parse_relation.h"
79 #include "parser/parse_type.h"
80 #include "parser/parse_utilcmd.h"
81 #include "parser/parser.h"
82 #include "partitioning/partbounds.h"
83 #include "partitioning/partdesc.h"
85 #include "rewrite/rewriteDefine.h"
86 #include "rewrite/rewriteHandler.h"
87 #include "rewrite/rewriteManip.h"
88 #include "storage/bufmgr.h"
89 #include "storage/lmgr.h"
90 #include "storage/lock.h"
91 #include "storage/predicate.h"
92 #include "storage/smgr.h"
93 #include "tcop/utility.h"
94 #include "utils/acl.h"
95 #include "utils/builtins.h"
96 #include "utils/fmgroids.h"
97 #include "utils/inval.h"
98 #include "utils/lsyscache.h"
99 #include "utils/memutils.h"
100 #include "utils/partcache.h"
101 #include "utils/relcache.h"
102 #include "utils/ruleutils.h"
103 #include "utils/snapmgr.h"
104 #include "utils/syscache.h"
105 #include "utils/timestamp.h"
106 #include "utils/typcache.h"
107 #include "utils/usercontext.h"
110 * ON COMMIT action list
112 typedef struct OnCommitItem
114 Oid relid
; /* relid of relation */
115 OnCommitAction oncommit
; /* what to do at end of xact */
118 * If this entry was created during the current transaction,
119 * creating_subid is the ID of the creating subxact; if created in a prior
120 * transaction, creating_subid is zero. If deleted during the current
121 * transaction, deleting_subid is the ID of the deleting subxact; if no
122 * deletion request is pending, deleting_subid is zero.
124 SubTransactionId creating_subid
;
125 SubTransactionId deleting_subid
;
128 static List
*on_commits
= NIL
;
132 * State information for ALTER TABLE
134 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
135 * structs, one for each table modified by the operation (the named table
136 * plus any child tables that are affected). We save lists of subcommands
137 * to apply to this table (possibly modified by parse transformation steps);
138 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
139 * necessary information is stored in the constraints and newvals lists.
141 * Phase 2 is divided into multiple passes; subcommands are executed in
142 * a pass determined by subcommand type.
145 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
146 #define AT_PASS_DROP 0 /* DROP (all flavors) */
147 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
148 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
149 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
150 /* We could support a RENAME COLUMN pass here, but not currently used */
151 #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
152 #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
153 #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
154 #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
155 #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
156 #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
157 #define AT_PASS_MISC 10 /* other stuff */
158 #define AT_NUM_PASSES 11
160 typedef struct AlteredTableInfo
162 /* Information saved before any work commences: */
163 Oid relid
; /* Relation to work on */
164 char relkind
; /* Its relkind */
165 TupleDesc oldDesc
; /* Pre-modification tuple descriptor */
168 * Transiently set during Phase 2, normally set to NULL.
170 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
171 * returns control. This can be exploited by ATExecCmd subroutines to
172 * close/reopen across transaction boundaries.
176 /* Information saved by Phase 1 for Phase 2: */
177 List
*subcmds
[AT_NUM_PASSES
]; /* Lists of AlterTableCmd */
178 /* Information saved by Phases 1/2 for Phase 3: */
179 List
*constraints
; /* List of NewConstraint */
180 List
*newvals
; /* List of NewColumnValue */
181 List
*afterStmts
; /* List of utility command parsetrees */
182 bool verify_new_notnull
; /* T if we should recheck NOT NULL */
183 int rewrite
; /* Reason for forced rewrite, if any */
184 Oid newAccessMethod
; /* new access method; 0 means no change */
185 Oid newTableSpace
; /* new tablespace; 0 means no change */
186 bool chgPersistence
; /* T if SET LOGGED/UNLOGGED is used */
187 char newrelpersistence
; /* if above is true */
188 Expr
*partition_constraint
; /* for attach partition validation */
189 /* true, if validating default due to some other attach/detach */
190 bool validate_default
;
191 /* Objects to rebuild after completing ALTER TYPE operations */
192 List
*changedConstraintOids
; /* OIDs of constraints to rebuild */
193 List
*changedConstraintDefs
; /* string definitions of same */
194 List
*changedIndexOids
; /* OIDs of indexes to rebuild */
195 List
*changedIndexDefs
; /* string definitions of same */
196 char *replicaIdentityIndex
; /* index to reset as REPLICA IDENTITY */
197 char *clusterOnIndex
; /* index to use for CLUSTER */
198 List
*changedStatisticsOids
; /* OIDs of statistics to rebuild */
199 List
*changedStatisticsDefs
; /* string definitions of same */
202 /* Struct describing one new constraint to check in Phase 3 scan */
203 /* Note: new not-null constraints are handled elsewhere */
204 typedef struct NewConstraint
206 char *name
; /* Constraint name, or NULL if none */
207 ConstrType contype
; /* CHECK or FOREIGN */
208 Oid refrelid
; /* PK rel, if FOREIGN */
209 Oid refindid
; /* OID of PK's index, if FOREIGN */
210 Oid conid
; /* OID of pg_constraint entry, if FOREIGN */
211 Node
*qual
; /* Check expr or CONSTR_FOREIGN Constraint */
212 ExprState
*qualstate
; /* Execution state for CHECK expr */
216 * Struct describing one new column value that needs to be computed during
217 * Phase 3 copy (this could be either a new column with a non-null default, or
218 * a column that we're changing the type of). Columns without such an entry
219 * are just copied from the old table during ATRewriteTable. Note that the
220 * expr is an expression over *old* table values, except when is_generated
221 * is true; then it is an expression over columns of the *new* tuple.
223 typedef struct NewColumnValue
225 AttrNumber attnum
; /* which column */
226 Expr
*expr
; /* expression to compute */
227 ExprState
*exprstate
; /* execution state */
228 bool is_generated
; /* is it a GENERATED expression? */
232 * Error-reporting support for RemoveRelations
234 struct dropmsgstrings
237 int nonexistent_code
;
238 const char *nonexistent_msg
;
239 const char *skipping_msg
;
240 const char *nota_msg
;
241 const char *drophint_msg
;
244 static const struct dropmsgstrings dropmsgstringarray
[] = {
246 ERRCODE_UNDEFINED_TABLE
,
247 gettext_noop("table \"%s\" does not exist"),
248 gettext_noop("table \"%s\" does not exist, skipping"),
249 gettext_noop("\"%s\" is not a table"),
250 gettext_noop("Use DROP TABLE to remove a table.")},
252 ERRCODE_UNDEFINED_TABLE
,
253 gettext_noop("sequence \"%s\" does not exist"),
254 gettext_noop("sequence \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a sequence"),
256 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
258 ERRCODE_UNDEFINED_TABLE
,
259 gettext_noop("view \"%s\" does not exist"),
260 gettext_noop("view \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a view"),
262 gettext_noop("Use DROP VIEW to remove a view.")},
264 ERRCODE_UNDEFINED_TABLE
,
265 gettext_noop("materialized view \"%s\" does not exist"),
266 gettext_noop("materialized view \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a materialized view"),
268 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
270 ERRCODE_UNDEFINED_OBJECT
,
271 gettext_noop("index \"%s\" does not exist"),
272 gettext_noop("index \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not an index"),
274 gettext_noop("Use DROP INDEX to remove an index.")},
275 {RELKIND_COMPOSITE_TYPE
,
276 ERRCODE_UNDEFINED_OBJECT
,
277 gettext_noop("type \"%s\" does not exist"),
278 gettext_noop("type \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a type"),
280 gettext_noop("Use DROP TYPE to remove a type.")},
281 {RELKIND_FOREIGN_TABLE
,
282 ERRCODE_UNDEFINED_OBJECT
,
283 gettext_noop("foreign table \"%s\" does not exist"),
284 gettext_noop("foreign table \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a foreign table"),
286 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
287 {RELKIND_PARTITIONED_TABLE
,
288 ERRCODE_UNDEFINED_TABLE
,
289 gettext_noop("table \"%s\" does not exist"),
290 gettext_noop("table \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a table"),
292 gettext_noop("Use DROP TABLE to remove a table.")},
293 {RELKIND_PARTITIONED_INDEX
,
294 ERRCODE_UNDEFINED_OBJECT
,
295 gettext_noop("index \"%s\" does not exist"),
296 gettext_noop("index \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not an index"),
298 gettext_noop("Use DROP INDEX to remove an index.")},
299 {'\0', 0, NULL
, NULL
, NULL
, NULL
}
302 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
303 struct DropRelationCallbackState
305 /* These fields are set by RemoveRelations: */
306 char expected_relkind
;
307 LOCKMODE heap_lockmode
;
308 /* These fields are state to track which subsidiary locks are held: */
311 /* These fields are passed back by RangeVarCallbackForDropRelation: */
313 char actual_relpersistence
;
316 /* Alter table target-type flags for ATSimplePermissions */
317 #define ATT_TABLE 0x0001
318 #define ATT_VIEW 0x0002
319 #define ATT_MATVIEW 0x0004
320 #define ATT_INDEX 0x0008
321 #define ATT_COMPOSITE_TYPE 0x0010
322 #define ATT_FOREIGN_TABLE 0x0020
323 #define ATT_PARTITIONED_INDEX 0x0040
324 #define ATT_SEQUENCE 0x0080
327 * ForeignTruncateInfo
329 * Information related to truncation of foreign tables. This is used for
330 * the elements in a hash table. It uses the server OID as lookup key,
331 * and includes a per-server list of all foreign tables involved in the
334 typedef struct ForeignTruncateInfo
338 } ForeignTruncateInfo
;
341 * Partition tables are expected to be dropped when the parent partitioned
342 * table gets dropped. Hence for partitioning we use AUTO dependency.
343 * Otherwise, for regular inheritance use NORMAL dependency.
345 #define child_dependency_type(child_is_partition) \
346 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
348 static void truncate_check_rel(Oid relid
, Form_pg_class reltuple
);
349 static void truncate_check_perms(Oid relid
, Form_pg_class reltuple
);
350 static void truncate_check_activity(Relation rel
);
351 static void RangeVarCallbackForTruncate(const RangeVar
*relation
,
352 Oid relId
, Oid oldRelId
, void *arg
);
353 static List
*MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
354 bool is_partition
, List
**supconstr
,
356 static List
*MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
);
357 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
);
358 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
359 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
360 bool child_is_partition
);
361 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
362 int32 seqNumber
, Relation inhRelation
,
363 bool child_is_partition
);
364 static int findAttrByName(const char *attributeName
, const List
*columns
);
365 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
366 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
367 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
368 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
370 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
371 bool recurse
, bool recursing
, LOCKMODE lockmode
);
372 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
373 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
375 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
376 Relation rel
, char *constrName
,
377 bool recurse
, bool recursing
, LOCKMODE lockmode
);
378 static int transformColumnNameList(Oid relId
, List
*colList
,
379 int16
*attnums
, Oid
*atttypids
);
380 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
382 int16
*attnums
, Oid
*atttypids
,
384 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
385 int numattrs
, int16
*attnums
,
387 static void checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
);
388 static CoercionPathType
findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
,
390 static void validateForeignKeyConstraint(char *conname
,
391 Relation rel
, Relation pkrel
,
392 Oid pkindOid
, Oid constraintOid
);
393 static void ATController(AlterTableStmt
*parsetree
,
394 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
395 AlterTableUtilityContext
*context
);
396 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
397 bool recurse
, bool recursing
, LOCKMODE lockmode
,
398 AlterTableUtilityContext
*context
);
399 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
400 AlterTableUtilityContext
*context
);
401 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
402 AlterTableCmd
*cmd
, LOCKMODE lockmode
, int cur_pass
,
403 AlterTableUtilityContext
*context
);
404 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
405 Relation rel
, AlterTableCmd
*cmd
,
406 bool recurse
, LOCKMODE lockmode
,
408 AlterTableUtilityContext
*context
);
409 static void ATRewriteTables(AlterTableStmt
*parsetree
,
410 List
**wqueue
, LOCKMODE lockmode
,
411 AlterTableUtilityContext
*context
);
412 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
);
413 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
414 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
415 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
416 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
417 AlterTableUtilityContext
*context
);
418 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
419 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
421 AlterTableUtilityContext
*context
);
422 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
423 DropBehavior behavior
);
424 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
425 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
426 AlterTableUtilityContext
*context
);
427 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
428 Relation rel
, AlterTableCmd
**cmd
,
429 bool recurse
, bool recursing
,
430 LOCKMODE lockmode
, int cur_pass
,
431 AlterTableUtilityContext
*context
);
432 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
434 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
435 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
436 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
438 static bool set_attnotnull(List
**wqueue
, Relation rel
,
439 AttrNumber attnum
, bool recurse
, LOCKMODE lockmode
);
440 static ObjectAddress
ATExecSetNotNull(List
**wqueue
, Relation rel
,
441 char *constrname
, char *colName
,
442 bool recurse
, bool recursing
,
443 List
**readyRels
, LOCKMODE lockmode
);
444 static ObjectAddress
ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
445 const char *colName
, LOCKMODE lockmode
);
446 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
447 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
448 List
*testConstraint
, List
*provenConstraint
);
449 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
450 Node
*newDefault
, LOCKMODE lockmode
);
451 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
453 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
454 Node
*def
, LOCKMODE lockmode
);
455 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
456 Node
*def
, LOCKMODE lockmode
);
457 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
458 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
459 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
460 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
461 Node
*newValue
, LOCKMODE lockmode
);
462 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
463 Node
*options
, bool isReset
, LOCKMODE lockmode
);
464 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
465 Node
*newValue
, LOCKMODE lockmode
);
466 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
467 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
468 AlterTableUtilityContext
*context
);
469 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
470 DropBehavior behavior
,
471 bool recurse
, bool recursing
,
472 bool missing_ok
, LOCKMODE lockmode
,
473 ObjectAddresses
*addrs
);
474 static void ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
475 LOCKMODE lockmode
, AlterTableUtilityContext
*context
);
476 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
477 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
478 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
479 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
480 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
481 AlteredTableInfo
*tab
, Relation rel
,
482 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
484 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
485 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
486 IndexStmt
*stmt
, LOCKMODE lockmode
);
487 static ObjectAddress
ATAddCheckNNConstraint(List
**wqueue
,
488 AlteredTableInfo
*tab
, Relation rel
,
490 bool recurse
, bool recursing
, bool is_readd
,
492 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
493 Relation rel
, Constraint
*fkconstraint
,
494 bool recurse
, bool recursing
,
496 static ObjectAddress
addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
,
497 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
498 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
499 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
500 int numfkdelsetcols
, int16
*fkdelsetcols
,
502 Oid parentDelTrigger
, Oid parentUpdTrigger
);
503 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
504 int numfksetcols
, const int16
*fksetcolsattnums
,
506 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
507 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
508 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
509 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
510 int numfkdelsetcols
, int16
*fkdelsetcols
,
511 bool old_check_ok
, LOCKMODE lockmode
,
512 Oid parentInsTrigger
, Oid parentUpdTrigger
);
513 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
514 Relation partitionRel
);
515 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
516 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
518 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
519 Constraint
*fkconstraint
, Oid constraintOid
,
521 Oid parentInsTrigger
, Oid parentUpdTrigger
,
522 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
523 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
524 Constraint
*fkconstraint
, Oid constraintOid
,
526 Oid parentDelTrigger
, Oid parentUpdTrigger
,
527 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
528 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
530 Oid parentConstrOid
, int numfks
,
531 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
533 Oid parentInsTrigger
,
534 Oid parentUpdTrigger
,
536 static void GetForeignKeyActionTriggers(Relation trigrel
,
537 Oid conoid
, Oid confrelid
, Oid conrelid
,
538 Oid
*deleteTriggerOid
,
539 Oid
*updateTriggerOid
);
540 static void GetForeignKeyCheckTriggers(Relation trigrel
,
541 Oid conoid
, Oid confrelid
, Oid conrelid
,
542 Oid
*insertTriggerOid
,
543 Oid
*updateTriggerOid
);
544 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
545 DropBehavior behavior
, bool recurse
,
546 bool missing_ok
, LOCKMODE lockmode
);
547 static ObjectAddress
dropconstraint_internal(Relation rel
,
548 HeapTuple constraintTup
, DropBehavior behavior
,
549 bool recurse
, bool recursing
,
550 bool missing_ok
, List
**readyRels
,
552 static void ATPrepAlterColumnType(List
**wqueue
,
553 AlteredTableInfo
*tab
, Relation rel
,
554 bool recurse
, bool recursing
,
555 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
556 AlterTableUtilityContext
*context
);
557 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
558 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
559 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
560 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
561 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
562 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
563 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
565 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
566 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
568 static void RebuildConstraintComment(AlteredTableInfo
*tab
, int pass
,
569 Oid objid
, Relation rel
, List
*domname
,
570 const char *conname
);
571 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
572 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
573 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
574 List
*options
, LOCKMODE lockmode
);
575 static void change_owner_fix_column_acls(Oid relationOid
,
576 Oid oldOwnerId
, Oid newOwnerId
);
577 static void change_owner_recurse_to_sequences(Oid relationOid
,
578 Oid newOwnerId
, LOCKMODE lockmode
);
579 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
581 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
582 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
583 static bool ATPrepChangePersistence(Relation rel
, bool toLogged
);
584 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
585 const char *tablespacename
, LOCKMODE lockmode
);
586 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
587 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
588 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
589 AlterTableType operation
,
591 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
592 char fires_when
, bool skip_system
, bool recurse
,
594 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
595 char fires_when
, LOCKMODE lockmode
);
596 static void ATPrepAddInherit(Relation child_rel
);
597 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
598 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
599 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
600 DependencyType deptype
);
601 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
602 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
603 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
604 static void ATExecGenericOptions(Relation rel
, List
*options
);
605 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
606 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
607 static ObjectAddress
ATExecSetCompression(Relation rel
,
608 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
610 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
611 static const char *storage_name(char c
);
613 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
614 Oid oldRelOid
, void *arg
);
615 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
616 Oid oldrelid
, void *arg
);
617 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
618 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
619 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
620 PartitionStrategy strategy
);
621 static void CreateInheritance(Relation child_rel
, Relation parent_rel
);
622 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
623 bool expect_detached
);
624 static void ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
,
626 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
628 AlterTableUtilityContext
*context
);
629 static void AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
);
630 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
631 List
*partConstraint
,
632 bool validate_default
);
633 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
634 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
635 static void DropClonedTriggersFromPartition(Oid partitionId
);
636 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
637 Relation rel
, RangeVar
*name
,
639 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
640 bool concurrent
, Oid defaultPartOid
);
641 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
642 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
644 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
645 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
646 Relation partitionTbl
);
647 static void verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partIdx
);
648 static List
*GetParentedForeignKeyRefs(Relation partition
);
649 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
650 static char GetAttributeCompression(Oid atttypid
, const char *compression
);
651 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
654 /* ----------------------------------------------------------------
656 * Creates a new relation.
658 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
659 * The other arguments are used to extend the behavior for other cases:
660 * relkind: relkind to assign to the new relation
661 * ownerId: if not InvalidOid, use this as the new relation's owner.
662 * typaddress: if not null, it's set to the pg_type entry's address.
663 * queryString: for error reporting
665 * Note that permissions checks are done against current user regardless of
666 * ownerId. A nonzero ownerId is used when someone is creating a relation
667 * "on behalf of" someone else, so we still want to see that the current user
668 * has permissions to do it.
670 * If successful, returns the address of the new relation.
671 * ----------------------------------------------------------------
674 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
675 ObjectAddress
*typaddress
, const char *queryString
)
677 char relname
[NAMEDATALEN
];
682 TupleDesc descriptor
;
684 List
*old_constraints
;
687 List
*cookedDefaults
;
693 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
695 ObjectAddress address
;
696 LOCKMODE parentLockmode
;
697 const char *accessMethod
= NULL
;
698 Oid accessMethodId
= InvalidOid
;
701 * Truncate relname to appropriate length (probably a waste of time, as
702 * parser should have done this already).
704 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
707 * Check consistency of arguments
709 if (stmt
->oncommit
!= ONCOMMIT_NOOP
710 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
712 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
713 errmsg("ON COMMIT can only be used on temporary tables")));
715 if (stmt
->partspec
!= NULL
)
717 if (relkind
!= RELKIND_RELATION
)
718 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
720 relkind
= RELKIND_PARTITIONED_TABLE
;
727 * Look up the namespace in which we are supposed to create the relation,
728 * check we have permission to create there, lock it against concurrent
729 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
730 * namespace is selected.
733 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
736 * Security check: disallow creating temp tables from security-restricted
737 * code. This is needed because calling code might not expect untrusted
738 * tables to appear in pg_temp at the front of its search path.
740 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
741 && InSecurityRestrictedOperation())
743 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
744 errmsg("cannot create temporary table within security-restricted operation")));
747 * Determine the lockmode to use when scanning parents. A self-exclusive
748 * lock is needed here.
750 * For regular inheritance, if two backends attempt to add children to the
751 * same parent simultaneously, and that parent has no pre-existing
752 * children, then both will attempt to update the parent's relhassubclass
753 * field, leading to a "tuple concurrently updated" error. Also, this
754 * interlocks against a concurrent ANALYZE on the parent table, which
755 * might otherwise be attempting to clear the parent's relhassubclass
756 * field, if its previous children were recently dropped.
758 * If the child table is a partition, then we instead grab an exclusive
759 * lock on the parent because its partition descriptor will be changed by
760 * addition of the new partition.
762 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
763 ShareUpdateExclusiveLock
);
765 /* Determine the list of OIDs of the parents. */
767 foreach(listptr
, stmt
->inhRelations
)
769 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
772 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
775 * Reject duplications in the list of parents.
777 if (list_member_oid(inheritOids
, parentOid
))
779 (errcode(ERRCODE_DUPLICATE_TABLE
),
780 errmsg("relation \"%s\" would be inherited from more than once",
781 get_rel_name(parentOid
))));
783 inheritOids
= lappend_oid(inheritOids
, parentOid
);
787 * Select tablespace to use: an explicitly indicated one, or (in the case
788 * of a partitioned table) the parent's, if it has one.
790 if (stmt
->tablespacename
)
792 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
794 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
796 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
797 errmsg("cannot specify default tablespace for partitioned relations")));
799 else if (stmt
->partbound
)
802 * For partitions, when no other tablespace is specified, we default
803 * the tablespace to the parent partitioned table's.
805 Assert(list_length(inheritOids
) == 1);
806 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
809 tablespaceId
= InvalidOid
;
811 /* still nothing? use the default */
812 if (!OidIsValid(tablespaceId
))
813 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
816 /* Check permissions except when using database's default */
817 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
821 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
823 if (aclresult
!= ACLCHECK_OK
)
824 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
825 get_tablespace_name(tablespaceId
));
828 /* In all cases disallow placing user relations in pg_global */
829 if (tablespaceId
== GLOBALTABLESPACE_OID
)
831 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
832 errmsg("only shared relations can be placed in pg_global tablespace")));
834 /* Identify user ID that will own the table */
835 if (!OidIsValid(ownerId
))
836 ownerId
= GetUserId();
839 * Parse and validate reloptions, if any.
841 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
847 (void) view_reloptions(reloptions
, true);
849 case RELKIND_PARTITIONED_TABLE
:
850 (void) partitioned_table_reloptions(reloptions
, true);
853 (void) heap_reloptions(relkind
, reloptions
, true);
856 if (stmt
->ofTypename
)
860 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
862 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
863 if (aclresult
!= ACLCHECK_OK
)
864 aclcheck_error_type(aclresult
, ofTypeId
);
867 ofTypeId
= InvalidOid
;
870 * Look up inheritance ancestors and generate relation schema, including
871 * inherited attributes. (Note that stmt->tableElts is destructively
872 * modified by MergeAttributes.)
875 MergeAttributes(stmt
->tableElts
, inheritOids
,
876 stmt
->relation
->relpersistence
,
877 stmt
->partbound
!= NULL
,
878 &old_constraints
, &old_notnulls
);
881 * Create a tuple descriptor from the relation schema. Note that this
882 * deals with column names, types, and in-descriptor NOT NULL flags, but
883 * not default values, NOT NULL or CHECK constraints; we handle those
886 descriptor
= BuildDescForRelation(stmt
->tableElts
);
889 * Find columns with default values and prepare for insertion of the
890 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
891 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
892 * while raw defaults go into a list of RawColumnDefault structs that will
893 * be processed by AddRelationNewConstraints. (We can't deal with raw
894 * expressions until we can do transformExpr.)
896 * We can set the atthasdef flags now in the tuple descriptor; this just
897 * saves StoreAttrDefault from having to do an immediate update of the
901 cookedDefaults
= NIL
;
904 foreach(listptr
, stmt
->tableElts
)
906 ColumnDef
*colDef
= lfirst(listptr
);
907 Form_pg_attribute attr
;
910 attr
= TupleDescAttr(descriptor
, attnum
- 1);
912 if (colDef
->raw_default
!= NULL
)
914 RawColumnDefault
*rawEnt
;
916 Assert(colDef
->cooked_default
== NULL
);
918 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
919 rawEnt
->attnum
= attnum
;
920 rawEnt
->raw_default
= colDef
->raw_default
;
921 rawEnt
->missingMode
= false;
922 rawEnt
->generated
= colDef
->generated
;
923 rawDefaults
= lappend(rawDefaults
, rawEnt
);
924 attr
->atthasdef
= true;
926 else if (colDef
->cooked_default
!= NULL
)
928 CookedConstraint
*cooked
;
930 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
931 cooked
->contype
= CONSTR_DEFAULT
;
932 cooked
->conoid
= InvalidOid
; /* until created */
934 cooked
->attnum
= attnum
;
935 cooked
->expr
= colDef
->cooked_default
;
936 cooked
->skip_validation
= false;
937 cooked
->is_local
= true; /* not used for defaults */
938 cooked
->inhcount
= 0; /* ditto */
939 cooked
->is_no_inherit
= false;
940 cookedDefaults
= lappend(cookedDefaults
, cooked
);
941 attr
->atthasdef
= true;
946 * If the statement hasn't specified an access method, but we're defining
947 * a type of relation that needs one, use the default.
949 if (stmt
->accessMethod
!= NULL
)
951 accessMethod
= stmt
->accessMethod
;
955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
956 errmsg("specifying a table access method is not supported on a partitioned table")));
958 else if (RELKIND_HAS_TABLE_AM(relkind
))
959 accessMethod
= default_table_access_method
;
961 /* look up the access method, verify it is for a table */
962 if (accessMethod
!= NULL
)
963 accessMethodId
= get_table_am_oid(accessMethod
, false);
966 * Create the relation. Inherited defaults and constraints are passed in
967 * for immediate handling --- since they don't need parsing, they can be
968 * stored immediately.
970 relationId
= heap_create_with_catalog(relname
,
979 list_concat(cookedDefaults
,
982 stmt
->relation
->relpersistence
,
988 allowSystemTableMods
,
994 * We must bump the command counter to make the newly-created relation
995 * tuple visible for opening.
997 CommandCounterIncrement();
1000 * Open the new relation and acquire exclusive lock on it. This isn't
1001 * really necessary for locking out other backends (since they can't see
1002 * the new rel anyway until we commit), but it keeps the lock manager from
1003 * complaining about deadlock risks.
1005 rel
= relation_open(relationId
, AccessExclusiveLock
);
1008 * Now add any newly specified column default and generation expressions
1009 * to the new relation. These are passed to us in the form of raw
1010 * parsetrees; we need to transform them to executable expression trees
1011 * before they can be added. The most convenient way to do that is to
1012 * apply the parser's transformExpr routine, but transformExpr doesn't
1013 * work unless we have a pre-existing relation. So, the transformation has
1014 * to be postponed to this final step of CREATE TABLE.
1016 * This needs to be before processing the partitioning clauses because
1017 * those could refer to generated columns.
1020 AddRelationNewConstraints(rel
, rawDefaults
, NIL
,
1021 true, true, false, queryString
);
1024 * Make column generation expressions visible for use by partitioning.
1026 CommandCounterIncrement();
1028 /* Process and store partition bound, if any. */
1029 if (stmt
->partbound
)
1031 PartitionBoundSpec
*bound
;
1033 Oid parentId
= linitial_oid(inheritOids
),
1037 ParseNamespaceItem
*nsitem
;
1039 /* Already have strong enough lock on the parent */
1040 parent
= table_open(parentId
, NoLock
);
1043 * We are going to try to validate the partition bound specification
1044 * against the partition key of parentRel, so it better have one.
1046 if (parent
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
1048 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1049 errmsg("\"%s\" is not partitioned",
1050 RelationGetRelationName(parent
))));
1053 * The partition constraint of the default partition depends on the
1054 * partition bounds of every other partition. It is possible that
1055 * another backend might be about to execute a query on the default
1056 * partition table, and that the query relies on previously cached
1057 * default partition constraints. We must therefore take a table lock
1058 * strong enough to prevent all queries on the default partition from
1059 * proceeding until we commit and send out a shared-cache-inval notice
1060 * that will make them update their index lists.
1062 * Order of locking: The relation being added won't be visible to
1063 * other backends until it is committed, hence here in
1064 * DefineRelation() the order of locking the default partition and the
1065 * relation being added does not matter. But at all other places we
1066 * need to lock the default relation before we lock the relation being
1067 * added or removed i.e. we should take the lock in same order at all
1068 * the places such that lock parent, lock default partition and then
1069 * lock the partition so as to avoid a deadlock.
1072 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent
,
1074 if (OidIsValid(defaultPartOid
))
1075 defaultRel
= table_open(defaultPartOid
, AccessExclusiveLock
);
1077 /* Transform the bound values */
1078 pstate
= make_parsestate(NULL
);
1079 pstate
->p_sourcetext
= queryString
;
1082 * Add an nsitem containing this relation, so that transformExpr
1083 * called on partition bound expressions is able to report errors
1084 * using a proper context.
1086 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
1087 NULL
, false, false);
1088 addNSItemToQuery(pstate
, nsitem
, false, true, true);
1090 bound
= transformPartitionBound(pstate
, parent
, stmt
->partbound
);
1093 * Check first that the new partition's bound is valid and does not
1094 * overlap with any of existing partitions of the parent.
1096 check_new_partition_bound(relname
, parent
, bound
, pstate
);
1099 * If the default partition exists, its partition constraints will
1100 * change after the addition of this new partition such that it won't
1101 * allow any row that qualifies for this new partition. So, check that
1102 * the existing data in the default partition satisfies the constraint
1103 * as it will exist after adding this partition.
1105 if (OidIsValid(defaultPartOid
))
1107 check_default_partition_contents(parent
, defaultRel
, bound
);
1108 /* Keep the lock until commit. */
1109 table_close(defaultRel
, NoLock
);
1112 /* Update the pg_class entry. */
1113 StorePartitionBound(rel
, parent
, bound
);
1115 table_close(parent
, NoLock
);
1118 /* Store inheritance information for new rel. */
1119 StoreCatalogInheritance(relationId
, inheritOids
, stmt
->partbound
!= NULL
);
1122 * Process the partitioning specification (if any) and store the partition
1123 * key information into the catalog.
1129 AttrNumber partattrs
[PARTITION_MAX_KEYS
];
1130 Oid partopclass
[PARTITION_MAX_KEYS
];
1131 Oid partcollation
[PARTITION_MAX_KEYS
];
1132 List
*partexprs
= NIL
;
1134 pstate
= make_parsestate(NULL
);
1135 pstate
->p_sourcetext
= queryString
;
1137 partnatts
= list_length(stmt
->partspec
->partParams
);
1139 /* Protect fixed-size arrays here and in executor */
1140 if (partnatts
> PARTITION_MAX_KEYS
)
1142 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
1143 errmsg("cannot partition using more than %d columns",
1144 PARTITION_MAX_KEYS
)));
1147 * We need to transform the raw parsetrees corresponding to partition
1148 * expressions into executable expression trees. Like column defaults
1149 * and CHECK constraints, we could not have done the transformation
1152 stmt
->partspec
= transformPartitionSpec(rel
, stmt
->partspec
);
1154 ComputePartitionAttrs(pstate
, rel
, stmt
->partspec
->partParams
,
1155 partattrs
, &partexprs
, partopclass
,
1156 partcollation
, stmt
->partspec
->strategy
);
1158 StorePartitionKey(rel
, stmt
->partspec
->strategy
, partnatts
, partattrs
,
1160 partopclass
, partcollation
);
1162 /* make it all visible */
1163 CommandCounterIncrement();
1167 * If we're creating a partition, create now all the indexes, triggers,
1168 * FKs defined in the parent.
1170 * We can't do it earlier, because DefineIndex wants to know the partition
1171 * key which we just stored.
1173 if (stmt
->partbound
)
1175 Oid parentId
= linitial_oid(inheritOids
);
1180 /* Already have strong enough lock on the parent */
1181 parent
= table_open(parentId
, NoLock
);
1182 idxlist
= RelationGetIndexList(parent
);
1185 * For each index in the parent table, create one in the partition
1187 foreach(cell
, idxlist
)
1189 Relation idxRel
= index_open(lfirst_oid(cell
), AccessShareLock
);
1194 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1196 if (idxRel
->rd_index
->indisunique
)
1198 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1199 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1200 RelationGetRelationName(parent
)),
1201 errdetail("Table \"%s\" contains indexes that are unique.",
1202 RelationGetRelationName(parent
))));
1205 index_close(idxRel
, AccessShareLock
);
1210 attmap
= build_attrmap_by_name(RelationGetDescr(rel
),
1211 RelationGetDescr(parent
),
1214 generateClonedIndexStmt(NULL
, idxRel
,
1215 attmap
, &constraintOid
);
1216 DefineIndex(RelationGetRelid(rel
),
1219 RelationGetRelid(idxRel
),
1222 false, false, false, false, false);
1224 index_close(idxRel
, AccessShareLock
);
1230 * If there are any row-level triggers, clone them to the new
1233 if (parent
->trigdesc
!= NULL
)
1234 CloneRowTriggersToPartition(parent
, rel
);
1237 * And foreign keys too. Note that because we're freshly creating the
1238 * table, there is no need to verify these new constraints.
1240 CloneForeignKeyConstraints(NULL
, parent
, rel
);
1242 table_close(parent
, NoLock
);
1246 * Now add any newly specified CHECK constraints to the new relation. Same
1247 * as for defaults above, but these need to come after partitioning is set
1250 if (stmt
->constraints
)
1251 AddRelationNewConstraints(rel
, NIL
, stmt
->constraints
,
1252 true, true, false, queryString
);
1255 * Finally, merge the not-null constraints that are declared directly with
1256 * those that come from parent relations (making sure to count inheritance
1257 * appropriately for each), create them, and set the attnotnull flag on
1258 * columns that don't yet have it.
1260 nncols
= AddRelationNotNullConstraints(rel
, stmt
->nnconstraints
,
1262 foreach(listptr
, nncols
)
1263 set_attnotnull(NULL
, rel
, lfirst_int(listptr
), false, NoLock
);
1265 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1268 * Clean up. We keep lock on new relation (although it shouldn't be
1269 * visible to anyone else anyway, until commit).
1271 relation_close(rel
, NoLock
);
1277 * BuildDescForRelation
1279 * Given a list of ColumnDef nodes, build a TupleDesc.
1281 * Note: tdtypeid will need to be filled in later on.
1284 BuildDescForRelation(const List
*columns
)
1298 * allocate a new tuple descriptor
1300 natts
= list_length(columns
);
1301 desc
= CreateTemplateTupleDesc(natts
);
1302 has_not_null
= false;
1308 ColumnDef
*entry
= lfirst(l
);
1309 AclResult aclresult
;
1310 Form_pg_attribute att
;
1313 * for each entry in the list, get the name and type information from
1314 * the list and have TupleDescInitEntry fill in the attribute
1315 * information we need.
1319 attname
= entry
->colname
;
1320 typenameTypeIdAndMod(NULL
, entry
->typeName
, &atttypid
, &atttypmod
);
1322 aclresult
= object_aclcheck(TypeRelationId
, atttypid
, GetUserId(), ACL_USAGE
);
1323 if (aclresult
!= ACLCHECK_OK
)
1324 aclcheck_error_type(aclresult
, atttypid
);
1326 attcollation
= GetColumnDefCollation(NULL
, entry
, atttypid
);
1327 attdim
= list_length(entry
->typeName
->arrayBounds
);
1328 if (attdim
> PG_INT16_MAX
)
1330 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1331 errmsg("too many array dimensions"));
1333 if (entry
->typeName
->setof
)
1335 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
1336 errmsg("column \"%s\" cannot be declared SETOF",
1339 TupleDescInitEntry(desc
, attnum
, attname
,
1340 atttypid
, atttypmod
, attdim
);
1341 att
= TupleDescAttr(desc
, attnum
- 1);
1343 /* Override TupleDescInitEntry's settings as requested */
1344 TupleDescInitEntryCollation(desc
, attnum
, attcollation
);
1346 /* Fill in additional stuff not handled by TupleDescInitEntry */
1347 att
->attnotnull
= entry
->is_not_null
;
1348 has_not_null
|= entry
->is_not_null
;
1349 att
->attislocal
= entry
->is_local
;
1350 att
->attinhcount
= entry
->inhcount
;
1351 att
->attidentity
= entry
->identity
;
1352 att
->attgenerated
= entry
->generated
;
1353 att
->attcompression
= GetAttributeCompression(att
->atttypid
, entry
->compression
);
1355 att
->attstorage
= entry
->storage
;
1356 else if (entry
->storage_name
)
1357 att
->attstorage
= GetAttributeStorage(att
->atttypid
, entry
->storage_name
);
1362 TupleConstr
*constr
= (TupleConstr
*) palloc0(sizeof(TupleConstr
));
1364 constr
->has_not_null
= true;
1365 constr
->has_generated_stored
= false;
1366 constr
->defval
= NULL
;
1367 constr
->missing
= NULL
;
1368 constr
->num_defval
= 0;
1369 constr
->check
= NULL
;
1370 constr
->num_check
= 0;
1371 desc
->constr
= constr
;
1375 desc
->constr
= NULL
;
1382 * Emit the right error or warning message for a "DROP" command issued on a
1383 * non-existent relation
1386 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1388 const struct dropmsgstrings
*rentry
;
1390 if (rel
->schemaname
!= NULL
&&
1391 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1396 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1397 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1402 (errmsg("schema \"%s\" does not exist, skipping",
1408 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1410 if (rentry
->kind
== rightkind
)
1415 (errcode(rentry
->nonexistent_code
),
1416 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1420 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1426 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1430 * Emit the right error message for a "DROP" command issued on a
1431 * relation of the wrong type
1434 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1436 const struct dropmsgstrings
*rentry
;
1437 const struct dropmsgstrings
*wentry
;
1439 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1440 if (rentry
->kind
== rightkind
)
1442 Assert(rentry
->kind
!= '\0');
1444 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1445 if (wentry
->kind
== wrongkind
)
1447 /* wrongkind could be something we don't have in our table... */
1450 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1451 errmsg(rentry
->nota_msg
, relname
),
1452 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1457 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1458 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1461 RemoveRelations(DropStmt
*drop
)
1463 ObjectAddresses
*objects
;
1467 LOCKMODE lockmode
= AccessExclusiveLock
;
1469 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1470 if (drop
->concurrent
)
1473 * Note that for temporary relations this lock may get upgraded later
1474 * on, but as no other session can access a temporary relation, this
1477 lockmode
= ShareUpdateExclusiveLock
;
1478 Assert(drop
->removeType
== OBJECT_INDEX
);
1479 if (list_length(drop
->objects
) != 1)
1481 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1482 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1483 if (drop
->behavior
== DROP_CASCADE
)
1485 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1486 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1490 * First we identify all the relations, then we delete them in a single
1491 * performMultipleDeletions() call. This is to avoid unwanted DROP
1492 * RESTRICT errors if one of the relations depends on another.
1495 /* Determine required relkind */
1496 switch (drop
->removeType
)
1499 relkind
= RELKIND_RELATION
;
1503 relkind
= RELKIND_INDEX
;
1506 case OBJECT_SEQUENCE
:
1507 relkind
= RELKIND_SEQUENCE
;
1511 relkind
= RELKIND_VIEW
;
1514 case OBJECT_MATVIEW
:
1515 relkind
= RELKIND_MATVIEW
;
1518 case OBJECT_FOREIGN_TABLE
:
1519 relkind
= RELKIND_FOREIGN_TABLE
;
1523 elog(ERROR
, "unrecognized drop object type: %d",
1524 (int) drop
->removeType
);
1525 relkind
= 0; /* keep compiler quiet */
1529 /* Lock and validate each relation; build a list of object addresses */
1530 objects
= new_object_addresses();
1532 foreach(cell
, drop
->objects
)
1534 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1537 struct DropRelationCallbackState state
;
1540 * These next few steps are a great deal like relation_openrv, but we
1541 * don't bother building a relcache entry since we don't need it.
1543 * Check for shared-cache-inval messages before trying to access the
1544 * relation. This is needed to cover the case where the name
1545 * identifies a rel that has been dropped and recreated since the
1546 * start of our transaction: if we don't flush the old syscache entry,
1547 * then we'll latch onto that entry and suffer an error later.
1549 AcceptInvalidationMessages();
1551 /* Look up the appropriate relation using namespace search. */
1552 state
.expected_relkind
= relkind
;
1553 state
.heap_lockmode
= drop
->concurrent
?
1554 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1555 /* We must initialize these fields to show that no locks are held: */
1556 state
.heapOid
= InvalidOid
;
1557 state
.partParentOid
= InvalidOid
;
1559 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1560 RangeVarCallbackForDropRelation
,
1564 if (!OidIsValid(relOid
))
1566 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1571 * Decide if concurrent mode needs to be used here or not. The
1572 * callback retrieved the rel's persistence for us.
1574 if (drop
->concurrent
&&
1575 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1577 Assert(list_length(drop
->objects
) == 1 &&
1578 drop
->removeType
== OBJECT_INDEX
);
1579 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1583 * Concurrent index drop cannot be used with partitioned indexes,
1586 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1587 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1589 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1590 errmsg("cannot drop partitioned index \"%s\" concurrently",
1594 * If we're told to drop a partitioned index, we must acquire lock on
1595 * all the children of its parent partitioned table before proceeding.
1596 * Otherwise we'd try to lock the child index partitions before their
1597 * tables, leading to potential deadlock against other sessions that
1598 * will lock those objects in the other order.
1600 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1601 (void) find_all_inheritors(state
.heapOid
,
1602 state
.heap_lockmode
,
1605 /* OK, we're ready to delete this one */
1606 obj
.classId
= RelationRelationId
;
1607 obj
.objectId
= relOid
;
1608 obj
.objectSubId
= 0;
1610 add_exact_object_address(&obj
, objects
);
1613 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1615 free_object_addresses(objects
);
1619 * Before acquiring a table lock, check whether we have sufficient rights.
1620 * In the case of DROP INDEX, also try to lock the table before the index.
1621 * Also, if the table to be dropped is a partition, we try to lock the parent
1625 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1629 struct DropRelationCallbackState
*state
;
1630 char expected_relkind
;
1632 Form_pg_class classform
;
1633 LOCKMODE heap_lockmode
;
1634 bool invalid_system_index
= false;
1636 state
= (struct DropRelationCallbackState
*) arg
;
1637 heap_lockmode
= state
->heap_lockmode
;
1640 * If we previously locked some other index's heap, and the name we're
1641 * looking up no longer refers to that relation, release the now-useless
1644 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1646 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1647 state
->heapOid
= InvalidOid
;
1651 * Similarly, if we previously locked some other partition's heap, and the
1652 * name we're looking up no longer refers to that relation, release the
1655 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1657 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1658 state
->partParentOid
= InvalidOid
;
1661 /* Didn't find a relation, so no need for locking or permission checks. */
1662 if (!OidIsValid(relOid
))
1665 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1666 if (!HeapTupleIsValid(tuple
))
1667 return; /* concurrently dropped, so nothing to do */
1668 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1669 is_partition
= classform
->relispartition
;
1671 /* Pass back some data to save lookups in RemoveRelations */
1672 state
->actual_relkind
= classform
->relkind
;
1673 state
->actual_relpersistence
= classform
->relpersistence
;
1676 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1677 * but RemoveRelations() can only pass one relkind for a given relation.
1678 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1679 * That means we must be careful before giving the wrong type error when
1680 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1681 * exists with indexes.
1683 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1684 expected_relkind
= RELKIND_RELATION
;
1685 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1686 expected_relkind
= RELKIND_INDEX
;
1688 expected_relkind
= classform
->relkind
;
1690 if (state
->expected_relkind
!= expected_relkind
)
1691 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1692 state
->expected_relkind
);
1694 /* Allow DROP to either table owner or schema owner */
1695 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1696 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1697 aclcheck_error(ACLCHECK_NOT_OWNER
,
1698 get_relkind_objtype(classform
->relkind
),
1702 * Check the case of a system index that might have been invalidated by a
1703 * failed concurrent process and allow its drop. For the time being, this
1704 * only concerns indexes of toast relations that became invalid during a
1705 * REINDEX CONCURRENTLY process.
1707 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1710 Form_pg_index indexform
;
1713 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1714 if (!HeapTupleIsValid(locTuple
))
1716 ReleaseSysCache(tuple
);
1720 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1721 indisvalid
= indexform
->indisvalid
;
1722 ReleaseSysCache(locTuple
);
1724 /* Mark object as being an invalid index of system catalogs */
1726 invalid_system_index
= true;
1729 /* In the case of an invalid index, it is fine to bypass this check */
1730 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1732 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1733 errmsg("permission denied: \"%s\" is a system catalog",
1736 ReleaseSysCache(tuple
);
1739 * In DROP INDEX, attempt to acquire lock on the parent table before
1740 * locking the index. index_drop() will need this anyway, and since
1741 * regular queries lock tables before their indexes, we risk deadlock if
1742 * we do it the other way around. No error if we don't find a pg_index
1743 * entry, though --- the relation may have been dropped. Note that this
1744 * code will execute for either plain or partitioned indexes.
1746 if (expected_relkind
== RELKIND_INDEX
&&
1747 relOid
!= oldRelOid
)
1749 state
->heapOid
= IndexGetRelation(relOid
, true);
1750 if (OidIsValid(state
->heapOid
))
1751 LockRelationOid(state
->heapOid
, heap_lockmode
);
1755 * Similarly, if the relation is a partition, we must acquire lock on its
1756 * parent before locking the partition. That's because queries lock the
1757 * parent before its partitions, so we risk deadlock if we do it the other
1760 if (is_partition
&& relOid
!= oldRelOid
)
1762 state
->partParentOid
= get_partition_parent(relOid
, true);
1763 if (OidIsValid(state
->partParentOid
))
1764 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1770 * Executes a TRUNCATE command.
1772 * This is a multi-relation truncate. We first open and grab exclusive
1773 * lock on all relations involved, checking permissions and otherwise
1774 * verifying that the relation is OK for truncation. Note that if relations
1775 * are foreign tables, at this stage, we have not yet checked that their
1776 * foreign data in external data sources are OK for truncation. These are
1777 * checked when foreign data are actually truncated later. In CASCADE mode,
1778 * relations having FK references to the targeted relations are automatically
1779 * added to the group; in RESTRICT mode, we check that all FK references are
1780 * internal to the group that's being truncated. Finally all the relations
1781 * are truncated and reindexed.
1784 ExecuteTruncate(TruncateStmt
*stmt
)
1788 List
*relids_logged
= NIL
;
1792 * Open, exclusive-lock, and check all the explicitly-specified relations
1794 foreach(cell
, stmt
->relations
)
1796 RangeVar
*rv
= lfirst(cell
);
1798 bool recurse
= rv
->inh
;
1800 LOCKMODE lockmode
= AccessExclusiveLock
;
1802 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1803 0, RangeVarCallbackForTruncate
,
1806 /* don't throw error for "TRUNCATE foo, foo" */
1807 if (list_member_oid(relids
, myrelid
))
1810 /* open the relation, we already hold a lock on it */
1811 rel
= table_open(myrelid
, NoLock
);
1814 * RangeVarGetRelidExtended() has done most checks with its callback,
1815 * but other checks with the now-opened Relation remain.
1817 truncate_check_activity(rel
);
1819 rels
= lappend(rels
, rel
);
1820 relids
= lappend_oid(relids
, myrelid
);
1822 /* Log this relation only if needed for logical decoding */
1823 if (RelationIsLogicallyLogged(rel
))
1824 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1831 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1833 foreach(child
, children
)
1835 Oid childrelid
= lfirst_oid(child
);
1837 if (list_member_oid(relids
, childrelid
))
1840 /* find_all_inheritors already got lock */
1841 rel
= table_open(childrelid
, NoLock
);
1844 * It is possible that the parent table has children that are
1845 * temp tables of other backends. We cannot safely access
1846 * such tables (because of buffering issues), and the best
1847 * thing to do is to silently ignore them. Note that this
1848 * check is the same as one of the checks done in
1849 * truncate_check_activity() called below, still it is kept
1850 * here for simplicity.
1852 if (RELATION_IS_OTHER_TEMP(rel
))
1854 table_close(rel
, lockmode
);
1859 * Inherited TRUNCATE commands perform access permission
1860 * checks on the parent table only. So we skip checking the
1861 * children's permissions and don't call
1862 * truncate_check_perms() here.
1864 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1865 truncate_check_activity(rel
);
1867 rels
= lappend(rels
, rel
);
1868 relids
= lappend_oid(relids
, childrelid
);
1870 /* Log this relation only if needed for logical decoding */
1871 if (RelationIsLogicallyLogged(rel
))
1872 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1875 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1877 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1878 errmsg("cannot truncate only a partitioned table"),
1879 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1882 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1883 stmt
->behavior
, stmt
->restart_seqs
, false);
1885 /* And close the rels */
1888 Relation rel
= (Relation
) lfirst(cell
);
1890 table_close(rel
, NoLock
);
1895 * ExecuteTruncateGuts
1897 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1898 * command (see above) as well as replication subscribers that execute a
1899 * replicated TRUNCATE action.
1901 * explicit_rels is the list of Relations to truncate that the command
1902 * specified. relids is the list of Oids corresponding to explicit_rels.
1903 * relids_logged is the list of Oids (a subset of relids) that require
1904 * WAL-logging. This is all a bit redundant, but the existing callers have
1905 * this information handy in this form.
1908 ExecuteTruncateGuts(List
*explicit_rels
,
1910 List
*relids_logged
,
1911 DropBehavior behavior
, bool restart_seqs
,
1912 bool run_as_table_owner
)
1915 List
*seq_relids
= NIL
;
1916 HTAB
*ft_htab
= NULL
;
1918 ResultRelInfo
*resultRelInfos
;
1919 ResultRelInfo
*resultRelInfo
;
1920 SubTransactionId mySubid
;
1925 * Check the explicitly-specified relations.
1927 * In CASCADE mode, suck in all referencing relations as well. This
1928 * requires multiple iterations to find indirectly-dependent relations. At
1929 * each phase, we need to exclusive-lock new rels before looking for their
1930 * dependencies, else we might miss something. Also, we check each rel as
1931 * soon as we open it, to avoid a faux pas such as holding lock for a long
1932 * time on a rel we have no permissions for.
1934 rels
= list_copy(explicit_rels
);
1935 if (behavior
== DROP_CASCADE
)
1941 newrelids
= heap_truncate_find_FKs(relids
);
1942 if (newrelids
== NIL
)
1943 break; /* nothing else to add */
1945 foreach(cell
, newrelids
)
1947 Oid relid
= lfirst_oid(cell
);
1950 rel
= table_open(relid
, AccessExclusiveLock
);
1952 (errmsg("truncate cascades to table \"%s\"",
1953 RelationGetRelationName(rel
))));
1954 truncate_check_rel(relid
, rel
->rd_rel
);
1955 truncate_check_perms(relid
, rel
->rd_rel
);
1956 truncate_check_activity(rel
);
1957 rels
= lappend(rels
, rel
);
1958 relids
= lappend_oid(relids
, relid
);
1960 /* Log this relation only if needed for logical decoding */
1961 if (RelationIsLogicallyLogged(rel
))
1962 relids_logged
= lappend_oid(relids_logged
, relid
);
1968 * Check foreign key references. In CASCADE mode, this should be
1969 * unnecessary since we just pulled in all the references; but as a
1970 * cross-check, do it anyway if in an Assert-enabled build.
1972 #ifdef USE_ASSERT_CHECKING
1973 heap_truncate_check_FKs(rels
, false);
1975 if (behavior
== DROP_RESTRICT
)
1976 heap_truncate_check_FKs(rels
, false);
1980 * If we are asked to restart sequences, find all the sequences, lock them
1981 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1982 * We want to do this early since it's pointless to do all the truncation
1983 * work only to fail on sequence permissions.
1989 Relation rel
= (Relation
) lfirst(cell
);
1990 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
1993 foreach(seqcell
, seqlist
)
1995 Oid seq_relid
= lfirst_oid(seqcell
);
1998 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
2000 /* This check must match AlterSequence! */
2001 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
2002 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
2003 RelationGetRelationName(seq_rel
));
2005 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
2007 relation_close(seq_rel
, NoLock
);
2012 /* Prepare to catch AFTER triggers. */
2013 AfterTriggerBeginQuery();
2016 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2017 * each relation. We don't need to call ExecOpenIndices, though.
2019 * We put the ResultRelInfos in the es_opened_result_relations list, even
2020 * though we don't have a range table and don't populate the
2021 * es_result_relations array. That's a bit bogus, but it's enough to make
2022 * ExecGetTriggerResultRel() find them.
2024 estate
= CreateExecutorState();
2025 resultRelInfos
= (ResultRelInfo
*)
2026 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
2027 resultRelInfo
= resultRelInfos
;
2030 Relation rel
= (Relation
) lfirst(cell
);
2032 InitResultRelInfo(resultRelInfo
,
2034 0, /* dummy rangetable index */
2037 estate
->es_opened_result_relations
=
2038 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
2043 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2044 * truncating (this is because one of them might throw an error). Also, if
2045 * we were to allow them to prevent statement execution, that would need
2046 * to be handled here.
2048 resultRelInfo
= resultRelInfos
;
2053 if (run_as_table_owner
)
2054 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2056 ExecBSTruncateTriggers(estate
, resultRelInfo
);
2057 if (run_as_table_owner
)
2058 RestoreUserContext(&ucxt
);
2063 * OK, truncate each table.
2065 mySubid
= GetCurrentSubTransactionId();
2069 Relation rel
= (Relation
) lfirst(cell
);
2071 /* Skip partitioned tables as there is nothing to do */
2072 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
2076 * Build the lists of foreign tables belonging to each foreign server
2077 * and pass each list to the foreign data wrapper's callback function,
2078 * so that each server can truncate its all foreign tables in bulk.
2079 * Each list is saved as a single entry in a hash table that uses the
2080 * server OID as lookup key.
2082 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
2084 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
2086 ForeignTruncateInfo
*ft_info
;
2088 /* First time through, initialize hashtable for foreign tables */
2093 memset(&hctl
, 0, sizeof(HASHCTL
));
2094 hctl
.keysize
= sizeof(Oid
);
2095 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
2096 hctl
.hcxt
= CurrentMemoryContext
;
2098 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
2099 32, /* start small and extend */
2101 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
2104 /* Find or create cached entry for the foreign table */
2105 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
2108 ft_info
->serverid
= serverid
;
2109 ft_info
->rels
= NIL
;
2113 * Save the foreign table in the entry of the server that the
2114 * foreign table belongs to.
2116 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2121 * Normally, we need a transaction-safe truncation here. However, if
2122 * the table was either created in the current (sub)transaction or has
2123 * a new relfilenumber in the current (sub)transaction, then we can
2124 * just truncate it in-place, because a rollback would cause the whole
2125 * table or the current physical file to be thrown away anyway.
2127 if (rel
->rd_createSubid
== mySubid
||
2128 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2130 /* Immediate, non-rollbackable truncation is OK */
2131 heap_truncate_one_rel(rel
);
2137 ReindexParams reindex_params
= {0};
2140 * This effectively deletes all rows in the table, and may be done
2141 * in a serializable transaction. In that case we must record a
2142 * rw-conflict in to this transaction from each transaction
2143 * holding a predicate lock on the table.
2145 CheckTableForSerializableConflictIn(rel
);
2148 * Need the full transaction-safe pushups.
2150 * Create a new empty storage file for the relation, and assign it
2151 * as the relfilenumber value. The old storage file is scheduled
2152 * for deletion at commit.
2154 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2156 heap_relid
= RelationGetRelid(rel
);
2159 * The same for the toast table, if any.
2161 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2162 if (OidIsValid(toast_relid
))
2164 Relation toastrel
= relation_open(toast_relid
,
2165 AccessExclusiveLock
);
2167 RelationSetNewRelfilenumber(toastrel
,
2168 toastrel
->rd_rel
->relpersistence
);
2169 table_close(toastrel
, NoLock
);
2173 * Reconstruct the indexes to match, and we're done.
2175 reindex_relation(heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2179 pgstat_count_truncate(rel
);
2182 /* Now go through the hash table, and truncate foreign tables */
2185 ForeignTruncateInfo
*ft_info
;
2186 HASH_SEQ_STATUS seq
;
2188 hash_seq_init(&seq
, ft_htab
);
2192 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2194 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2196 /* truncate_check_rel() has checked that already */
2197 Assert(routine
->ExecForeignTruncate
!= NULL
);
2199 routine
->ExecForeignTruncate(ft_info
->rels
,
2206 hash_destroy(ft_htab
);
2212 * Restart owned sequences if we were asked to.
2214 foreach(cell
, seq_relids
)
2216 Oid seq_relid
= lfirst_oid(cell
);
2218 ResetSequence(seq_relid
);
2222 * Write a WAL record to allow this set of actions to be logically
2225 * Assemble an array of relids so we can write a single WAL record for the
2228 if (relids_logged
!= NIL
)
2230 xl_heap_truncate xlrec
;
2233 /* should only get here if wal_level >= logical */
2234 Assert(XLogLogicalInfoActive());
2236 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2237 foreach(cell
, relids_logged
)
2238 logrelids
[i
++] = lfirst_oid(cell
);
2240 xlrec
.dbId
= MyDatabaseId
;
2241 xlrec
.nrelids
= list_length(relids_logged
);
2243 if (behavior
== DROP_CASCADE
)
2244 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2246 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2249 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2250 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2252 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2254 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2258 * Process all AFTER STATEMENT TRUNCATE triggers.
2260 resultRelInfo
= resultRelInfos
;
2265 if (run_as_table_owner
)
2266 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2268 ExecASTruncateTriggers(estate
, resultRelInfo
);
2269 if (run_as_table_owner
)
2270 RestoreUserContext(&ucxt
);
2274 /* Handle queued AFTER triggers */
2275 AfterTriggerEndQuery(estate
);
2277 /* We can clean up the EState now */
2278 FreeExecutorState(estate
);
2281 * Close any rels opened by CASCADE (can't do this while EState still
2284 rels
= list_difference_ptr(rels
, explicit_rels
);
2287 Relation rel
= (Relation
) lfirst(cell
);
2289 table_close(rel
, NoLock
);
2294 * Check that a given relation is safe to truncate. Subroutine for
2295 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2298 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2300 char *relname
= NameStr(reltuple
->relname
);
2303 * Only allow truncate on regular tables, foreign tables using foreign
2304 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2305 * latter are only being included here for the following checks; no
2306 * physical truncation will occur in their case.).
2308 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2310 Oid serverid
= GetForeignServerIdByRelId(relid
);
2311 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2313 if (!fdwroutine
->ExecForeignTruncate
)
2315 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2316 errmsg("cannot truncate foreign table \"%s\"",
2319 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2320 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2322 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2323 errmsg("\"%s\" is not a table", relname
)));
2326 * Most system catalogs can't be truncated at all, or at least not unless
2327 * allow_system_table_mods=on. As an exception, however, we allow
2328 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2329 * to change its relfilenode to match the old cluster, and allowing a
2330 * TRUNCATE command to be executed is the easiest way of doing that.
2332 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2333 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2335 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2336 errmsg("permission denied: \"%s\" is a system catalog",
2339 InvokeObjectTruncateHook(relid
);
2343 * Check that current user has the permission to truncate given relation.
2346 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2348 char *relname
= NameStr(reltuple
->relname
);
2349 AclResult aclresult
;
2351 /* Permissions checks */
2352 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2353 if (aclresult
!= ACLCHECK_OK
)
2354 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2359 * Set of extra sanity checks to check if a given relation is safe to
2360 * truncate. This is split with truncate_check_rel() as
2361 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2364 truncate_check_activity(Relation rel
)
2367 * Don't allow truncate on temp tables of other backends ... their local
2368 * buffer manager is not going to cope.
2370 if (RELATION_IS_OTHER_TEMP(rel
))
2372 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2373 errmsg("cannot truncate temporary tables of other sessions")));
2376 * Also check for active uses of the relation in the current transaction,
2377 * including open scans and pending AFTER trigger events.
2379 CheckTableNotInUse(rel
, "TRUNCATE");
2384 * returns the name corresponding to a typstorage/attstorage enum value
2387 storage_name(char c
)
2391 case TYPSTORAGE_PLAIN
:
2393 case TYPSTORAGE_EXTERNAL
:
2395 case TYPSTORAGE_EXTENDED
:
2397 case TYPSTORAGE_MAIN
:
2406 * Returns new schema given initial schema and superclasses.
2409 * 'columns' is the column/attribute definition for the table. (It's a list
2410 * of ColumnDef's.) It is destructively changed.
2411 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2412 * 'relpersistence' is the persistence type of the table.
2413 * 'is_partition' tells if the table is a partition.
2416 * 'supconstr' receives a list of constraints belonging to the parents,
2417 * updated as necessary to be valid for the child.
2418 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2419 * constraints coming from inheritance parents.
2422 * Completed schema list.
2425 * The order in which the attributes are inherited is very important.
2426 * Intuitively, the inherited attributes should come first. If a table
2427 * inherits from multiple parents, the order of those attributes are
2428 * according to the order of the parents specified in CREATE TABLE.
2430 * Here's an example:
2432 * create table person (name text, age int4, location point);
2433 * create table emp (salary int4, manager text) inherits(person);
2434 * create table student (gpa float8) inherits (person);
2435 * create table stud_emp (percent int4) inherits (emp, student);
2437 * The order of the attributes of stud_emp is:
2439 * person {1:name, 2:age, 3:location}
2441 * {6:gpa} student emp {4:salary, 5:manager}
2443 * stud_emp {7:percent}
2445 * If the same attribute name appears multiple times, then it appears
2446 * in the result table in the proper location for its first appearance.
2448 * Constraints (including not-null constraints) for the child table
2449 * are the union of all relevant constraints, from both the child schema
2450 * and parent tables. In addition, in legacy inheritance, each column that
2451 * appears in a primary key in any of the parents also gets a NOT NULL
2452 * constraint (partitioning doesn't need this, because the PK itself gets
2455 * The default value for a child column is defined as:
2456 * (1) If the child schema specifies a default, that value is used.
2457 * (2) If neither the child nor any parent specifies a default, then
2458 * the column will not have a default.
2459 * (3) If conflicting defaults are inherited from different parents
2460 * (and not overridden by the child), an error is raised.
2461 * (4) Otherwise the inherited default is used.
2463 * Note that the default-value infrastructure is used for generated
2464 * columns' expressions too, so most of the preceding paragraph applies
2465 * to generation expressions too. We insist that a child column be
2466 * generated if and only if its parent(s) are, but it need not have
2467 * the same generation expression.
2471 MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
2472 bool is_partition
, List
**supconstr
, List
**supnotnulls
)
2474 List
*inh_columns
= NIL
;
2475 List
*constraints
= NIL
;
2476 List
*nnconstraints
= NIL
;
2477 bool have_bogus_defaults
= false;
2479 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2480 List
*saved_columns
= NIL
;
2484 * Check for and reject tables with too many columns. We perform this
2485 * check relatively early for two reasons: (a) we don't run the risk of
2486 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2487 * okay if we're processing <= 1600 columns, but could take minutes to
2488 * execute if the user attempts to create a table with hundreds of
2489 * thousands of columns.
2491 * Note that we also need to check that we do not exceed this figure after
2492 * including columns from inherited relations.
2494 if (list_length(columns
) > MaxHeapAttributeNumber
)
2496 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2497 errmsg("tables can have at most %d columns",
2498 MaxHeapAttributeNumber
)));
2501 * Check for duplicate names in the explicit list of attributes.
2503 * Although we might consider merging such entries in the same way that we
2504 * handle name conflicts for inherited attributes, it seems to make more
2505 * sense to assume such conflicts are errors.
2507 * We don't use foreach() here because we have two nested loops over the
2508 * columns list, with possible element deletions in the inner one. If we
2509 * used foreach_delete_current() it could only fix up the state of one of
2510 * the loops, so it seems cleaner to use looping over list indexes for
2511 * both loops. Note that any deletion will happen beyond where the outer
2512 * loop is, so its index never needs adjustment.
2514 for (int coldefpos
= 0; coldefpos
< list_length(columns
); coldefpos
++)
2516 ColumnDef
*coldef
= list_nth_node(ColumnDef
, columns
, coldefpos
);
2518 if (!is_partition
&& coldef
->typeName
== NULL
)
2521 * Typed table column option that does not belong to a column from
2522 * the type. This works because the columns from the type come
2523 * first in the list. (We omit this check for partition column
2524 * lists; those are processed separately below.)
2527 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2528 errmsg("column \"%s\" does not exist",
2532 /* restpos scans all entries beyond coldef; incr is in loop body */
2533 for (int restpos
= coldefpos
+ 1; restpos
< list_length(columns
);)
2535 ColumnDef
*restdef
= list_nth_node(ColumnDef
, columns
, restpos
);
2537 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2539 if (coldef
->is_from_type
)
2542 * merge the column options into the column from the type
2544 coldef
->is_not_null
= restdef
->is_not_null
;
2545 coldef
->raw_default
= restdef
->raw_default
;
2546 coldef
->cooked_default
= restdef
->cooked_default
;
2547 coldef
->constraints
= restdef
->constraints
;
2548 coldef
->is_from_type
= false;
2549 columns
= list_delete_nth_cell(columns
, restpos
);
2553 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2554 errmsg("column \"%s\" specified more than once",
2563 * In case of a partition, there are no new column definitions, only dummy
2564 * ColumnDefs created for column constraints. Set them aside for now and
2565 * process them at the end.
2569 saved_columns
= columns
;
2574 * Scan the parents left-to-right, and merge their attributes to form a
2575 * list of inherited columns (inh_columns).
2580 Oid parent
= lfirst_oid(lc
);
2582 TupleDesc tupleDesc
;
2583 TupleConstr
*constr
;
2585 List
*inherited_defaults
;
2586 List
*cols_with_defaults
;
2591 Bitmapset
*nncols
= NULL
;
2593 /* caller already got lock */
2594 relation
= table_open(parent
, NoLock
);
2597 * Check for active uses of the parent partitioned table in the
2598 * current transaction, such as being used in some manner by an
2599 * enclosing command.
2602 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2605 * We do not allow partitioned tables and partitions to participate in
2606 * regular inheritance.
2608 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !is_partition
)
2610 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2611 errmsg("cannot inherit from partitioned table \"%s\"",
2612 RelationGetRelationName(relation
))));
2613 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2615 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2616 errmsg("cannot inherit from partition \"%s\"",
2617 RelationGetRelationName(relation
))));
2619 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2620 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2621 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2623 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2624 errmsg("inherited relation \"%s\" is not a table or foreign table",
2625 RelationGetRelationName(relation
))));
2628 * If the parent is permanent, so must be all of its partitions. Note
2629 * that inheritance allows that case.
2632 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2633 relpersistence
== RELPERSISTENCE_TEMP
)
2635 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2636 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2637 RelationGetRelationName(relation
))));
2639 /* Permanent rels cannot inherit from temporary ones */
2640 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2641 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2643 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2644 errmsg(!is_partition
2645 ? "cannot inherit from temporary relation \"%s\""
2646 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2647 RelationGetRelationName(relation
))));
2649 /* If existing rel is temp, it must belong to this session */
2650 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2651 !relation
->rd_islocaltemp
)
2653 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2654 errmsg(!is_partition
2655 ? "cannot inherit from temporary relation of another session"
2656 : "cannot create as partition of temporary relation of another session")));
2659 * We should have an UNDER permission flag for this, but for now,
2660 * demand that creator of a child table own the parent.
2662 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2663 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2664 RelationGetRelationName(relation
));
2666 tupleDesc
= RelationGetDescr(relation
);
2667 constr
= tupleDesc
->constr
;
2670 * newattmap->attnums[] will contain the child-table attribute numbers
2671 * for the attributes of this parent table. (They are not the same
2672 * for parents after the first one, nor if we have dropped columns.)
2674 newattmap
= make_attrmap(tupleDesc
->natts
);
2676 /* We can't process inherited defaults until newattmap is complete. */
2677 inherited_defaults
= cols_with_defaults
= NIL
;
2680 * All columns that are part of the parent's primary key need to be
2681 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2682 * constraint (if they don't have one already). Also, we request
2683 * attnotnull on columns that have a not-null constraint that's not
2684 * marked NO INHERIT.
2686 pkattrs
= RelationGetIndexAttrBitmap(relation
,
2687 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
2688 nnconstrs
= RelationGetNotNullConstraints(RelationGetRelid(relation
), true);
2689 foreach(lc1
, nnconstrs
)
2690 nncols
= bms_add_member(nncols
,
2691 ((CookedConstraint
*) lfirst(lc1
))->attnum
);
2693 for (AttrNumber parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2696 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2698 char *attributeName
= NameStr(attribute
->attname
);
2703 * Ignore dropped columns in the parent.
2705 if (attribute
->attisdropped
)
2706 continue; /* leave newattmap->attnums entry as zero */
2709 * Does it conflict with some previously inherited column?
2711 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2712 if (exist_attno
> 0)
2719 * Yes, try to merge the two column definitions.
2722 (errmsg("merging multiple inherited definitions of column \"%s\"",
2724 def
= (ColumnDef
*) list_nth(inh_columns
, exist_attno
- 1);
2727 * Must have the same type and typmod
2729 typenameTypeIdAndMod(NULL
, def
->typeName
, &defTypeId
, &deftypmod
);
2730 if (defTypeId
!= attribute
->atttypid
||
2731 deftypmod
!= attribute
->atttypmod
)
2733 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2734 errmsg("inherited column \"%s\" has a type conflict",
2736 errdetail("%s versus %s",
2737 format_type_with_typemod(defTypeId
,
2739 format_type_with_typemod(attribute
->atttypid
,
2740 attribute
->atttypmod
))));
2743 * Must have the same collation
2745 defCollId
= GetColumnDefCollation(NULL
, def
, defTypeId
);
2746 if (defCollId
!= attribute
->attcollation
)
2748 (errcode(ERRCODE_COLLATION_MISMATCH
),
2749 errmsg("inherited column \"%s\" has a collation conflict",
2751 errdetail("\"%s\" versus \"%s\"",
2752 get_collation_name(defCollId
),
2753 get_collation_name(attribute
->attcollation
))));
2756 * Copy/check storage parameter
2758 if (def
->storage
== 0)
2759 def
->storage
= attribute
->attstorage
;
2760 else if (def
->storage
!= attribute
->attstorage
)
2762 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2763 errmsg("inherited column \"%s\" has a storage parameter conflict",
2765 errdetail("%s versus %s",
2766 storage_name(def
->storage
),
2767 storage_name(attribute
->attstorage
))));
2770 * Copy/check compression parameter
2772 if (CompressionMethodIsValid(attribute
->attcompression
))
2774 const char *compression
=
2775 GetCompressionMethodName(attribute
->attcompression
);
2777 if (def
->compression
== NULL
)
2778 def
->compression
= pstrdup(compression
);
2779 else if (strcmp(def
->compression
, compression
) != 0)
2781 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2782 errmsg("column \"%s\" has a compression method conflict",
2784 errdetail("%s versus %s", def
->compression
, compression
)));
2788 * In regular inheritance, columns in the parent's primary key
2789 * get an extra not-null constraint. Partitioning doesn't
2790 * need this, because the PK itself is going to be cloned to
2793 if (!is_partition
&&
2794 bms_is_member(parent_attno
- FirstLowInvalidHeapAttributeNumber
,
2797 CookedConstraint
*nn
;
2799 nn
= palloc(sizeof(CookedConstraint
));
2800 nn
->contype
= CONSTR_NOTNULL
;
2801 nn
->conoid
= InvalidOid
;
2803 nn
->attnum
= exist_attno
;
2805 nn
->skip_validation
= false;
2806 nn
->is_local
= false;
2808 nn
->is_no_inherit
= false;
2810 nnconstraints
= lappend(nnconstraints
, nn
);
2814 * mark attnotnull if parent has it and it's not NO INHERIT
2816 if (bms_is_member(parent_attno
, nncols
) ||
2817 bms_is_member(parent_attno
- FirstLowInvalidHeapAttributeNumber
,
2819 def
->is_not_null
= true;
2822 * Check for GENERATED conflicts
2824 if (def
->generated
!= attribute
->attgenerated
)
2826 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2827 errmsg("inherited column \"%s\" has a generation conflict",
2831 * Default and other constraints are handled below
2835 if (def
->inhcount
< 0)
2837 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2838 errmsg("too many inheritance parents"));
2840 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2845 * No, create a new inherited column
2847 def
= makeColumnDef(attributeName
, attribute
->atttypid
,
2848 attribute
->atttypmod
, attribute
->attcollation
);
2850 def
->is_local
= false;
2851 /* mark attnotnull if parent has it and it's not NO INHERIT */
2852 if (bms_is_member(parent_attno
, nncols
) ||
2853 bms_is_member(parent_attno
- FirstLowInvalidHeapAttributeNumber
,
2855 def
->is_not_null
= true;
2856 def
->storage
= attribute
->attstorage
;
2857 def
->generated
= attribute
->attgenerated
;
2858 if (CompressionMethodIsValid(attribute
->attcompression
))
2860 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2861 inh_columns
= lappend(inh_columns
, def
);
2862 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2865 * In regular inheritance, columns in the parent's primary key
2866 * get an extra not-null constraint. Partitioning doesn't
2867 * need this, because the PK itself is going to be cloned to
2870 if (!is_partition
&&
2871 bms_is_member(parent_attno
-
2872 FirstLowInvalidHeapAttributeNumber
,
2875 CookedConstraint
*nn
;
2877 nn
= palloc(sizeof(CookedConstraint
));
2878 nn
->contype
= CONSTR_NOTNULL
;
2879 nn
->conoid
= InvalidOid
;
2881 nn
->attnum
= newattmap
->attnums
[parent_attno
- 1];
2883 nn
->skip_validation
= false;
2884 nn
->is_local
= false;
2886 nn
->is_no_inherit
= false;
2888 nnconstraints
= lappend(nnconstraints
, nn
);
2893 * Locate default/generation expression if any
2895 if (attribute
->atthasdef
)
2899 this_default
= TupleDescGetDefault(tupleDesc
, parent_attno
);
2900 if (this_default
== NULL
)
2901 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2902 parent_attno
, RelationGetRelationName(relation
));
2905 * If it's a GENERATED default, it might contain Vars that
2906 * need to be mapped to the inherited column(s)' new numbers.
2907 * We can't do that till newattmap is ready, so just remember
2908 * all the inherited default expressions for the moment.
2910 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2911 cols_with_defaults
= lappend(cols_with_defaults
, def
);
2916 * Now process any inherited default expressions, adjusting attnos
2917 * using the completed newattmap map.
2919 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2921 Node
*this_default
= (Node
*) lfirst(lc1
);
2922 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2923 bool found_whole_row
;
2925 /* Adjust Vars to match new table's column numbering */
2926 this_default
= map_variable_attnos(this_default
,
2929 InvalidOid
, &found_whole_row
);
2932 * For the moment we have to reject whole-row variables. We could
2933 * convert them, if we knew the new table's rowtype OID, but that
2934 * hasn't been assigned yet. (A variable could only appear in a
2935 * generation expression, so the error message is correct.)
2937 if (found_whole_row
)
2939 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2940 errmsg("cannot convert whole-row table reference"),
2941 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2943 RelationGetRelationName(relation
))));
2946 * If we already had a default from some prior parent, check to
2947 * see if they are the same. If so, no problem; if not, mark the
2948 * column as having a bogus default. Below, we will complain if
2949 * the bogus default isn't overridden by the child columns.
2951 Assert(def
->raw_default
== NULL
);
2952 if (def
->cooked_default
== NULL
)
2953 def
->cooked_default
= this_default
;
2954 else if (!equal(def
->cooked_default
, this_default
))
2956 def
->cooked_default
= &bogus_marker
;
2957 have_bogus_defaults
= true;
2962 * Now copy the CHECK constraints of this parent, adjusting attnos
2963 * using the completed newattmap map. Identically named constraints
2964 * are merged if possible, else we throw error.
2966 if (constr
&& constr
->num_check
> 0)
2968 ConstrCheck
*check
= constr
->check
;
2970 for (int i
= 0; i
< constr
->num_check
; i
++)
2972 char *name
= check
[i
].ccname
;
2974 bool found_whole_row
;
2976 /* ignore if the constraint is non-inheritable */
2977 if (check
[i
].ccnoinherit
)
2980 /* Adjust Vars to match new table's column numbering */
2981 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2984 InvalidOid
, &found_whole_row
);
2987 * For the moment we have to reject whole-row variables. We
2988 * could convert them, if we knew the new table's rowtype OID,
2989 * but that hasn't been assigned yet.
2991 if (found_whole_row
)
2993 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2994 errmsg("cannot convert whole-row table reference"),
2995 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2997 RelationGetRelationName(relation
))));
2999 constraints
= MergeCheckConstraint(constraints
, name
, expr
);
3004 * Also copy the not-null constraints from this parent. The
3005 * attnotnull markings were already installed above.
3007 foreach(lc1
, nnconstrs
)
3009 CookedConstraint
*nn
= lfirst(lc1
);
3011 Assert(nn
->contype
== CONSTR_NOTNULL
);
3013 nn
->attnum
= newattmap
->attnums
[nn
->attnum
- 1];
3014 nn
->is_local
= false;
3017 nnconstraints
= lappend(nnconstraints
, nn
);
3020 free_attrmap(newattmap
);
3023 * Close the parent rel, but keep our lock on it until xact commit.
3024 * That will prevent someone else from deleting or ALTERing the parent
3025 * before the child is committed.
3027 table_close(relation
, NoLock
);
3031 * If we had no inherited attributes, the result columns are just the
3032 * explicitly declared columns. Otherwise, we need to merge the declared
3033 * columns into the inherited column list. Although, we never have any
3034 * explicitly declared columns if the table is a partition.
3036 if (inh_columns
!= NIL
)
3038 int newcol_attno
= 0;
3040 foreach(lc
, columns
)
3042 ColumnDef
*newdef
= lfirst(lc
);
3043 char *attributeName
= newdef
->colname
;
3049 * Does it conflict with some previously inherited column?
3051 exist_attno
= findAttrByName(attributeName
, inh_columns
);
3052 if (exist_attno
> 0)
3063 * Partitions have only one parent and have no column
3064 * definitions of their own, so conflict should never occur.
3066 Assert(!is_partition
);
3069 * Yes, try to merge the two column definitions.
3071 if (exist_attno
== newcol_attno
)
3073 (errmsg("merging column \"%s\" with inherited definition",
3077 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
3078 errdetail("User-specified column moved to the position of the inherited column.")));
3079 def
= (ColumnDef
*) list_nth(inh_columns
, exist_attno
- 1);
3082 * Must have the same type and typmod
3084 typenameTypeIdAndMod(NULL
, def
->typeName
, &defTypeId
, &deftypmod
);
3085 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newTypeId
, &newtypmod
);
3086 if (defTypeId
!= newTypeId
|| deftypmod
!= newtypmod
)
3088 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3089 errmsg("column \"%s\" has a type conflict",
3091 errdetail("%s versus %s",
3092 format_type_with_typemod(defTypeId
,
3094 format_type_with_typemod(newTypeId
,
3098 * Must have the same collation
3100 defcollid
= GetColumnDefCollation(NULL
, def
, defTypeId
);
3101 newcollid
= GetColumnDefCollation(NULL
, newdef
, newTypeId
);
3102 if (defcollid
!= newcollid
)
3104 (errcode(ERRCODE_COLLATION_MISMATCH
),
3105 errmsg("column \"%s\" has a collation conflict",
3107 errdetail("\"%s\" versus \"%s\"",
3108 get_collation_name(defcollid
),
3109 get_collation_name(newcollid
))));
3112 * Identity is never inherited. The new column can have an
3113 * identity definition, so we always just take that one.
3115 def
->identity
= newdef
->identity
;
3118 * Copy storage parameter
3120 if (def
->storage
== 0)
3121 def
->storage
= newdef
->storage
;
3122 else if (newdef
->storage
!= 0 && def
->storage
!= newdef
->storage
)
3124 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3125 errmsg("column \"%s\" has a storage parameter conflict",
3127 errdetail("%s versus %s",
3128 storage_name(def
->storage
),
3129 storage_name(newdef
->storage
))));
3132 * Copy compression parameter
3134 if (def
->compression
== NULL
)
3135 def
->compression
= newdef
->compression
;
3136 else if (newdef
->compression
!= NULL
)
3138 if (strcmp(def
->compression
, newdef
->compression
) != 0)
3140 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3141 errmsg("column \"%s\" has a compression method conflict",
3143 errdetail("%s versus %s", def
->compression
, newdef
->compression
)));
3147 * Merge of not-null constraints = OR 'em together
3149 def
->is_not_null
|= newdef
->is_not_null
;
3152 * Check for conflicts related to generated columns.
3154 * If the parent column is generated, the child column will be
3155 * made a generated column if it isn't already. If it is a
3156 * generated column, we'll take its generation expression in
3157 * preference to the parent's. We must check that the child
3158 * column doesn't specify a default value or identity, which
3159 * matches the rules for a single column in parse_utilcmd.c.
3161 * Conversely, if the parent column is not generated, the
3162 * child column can't be either. (We used to allow that, but
3163 * it results in being able to override the generation
3164 * expression via UPDATEs through the parent.)
3168 if (newdef
->raw_default
&& !newdef
->generated
)
3170 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3171 errmsg("column \"%s\" inherits from generated column but specifies default",
3173 if (newdef
->identity
)
3175 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3176 errmsg("column \"%s\" inherits from generated column but specifies identity",
3181 if (newdef
->generated
)
3183 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3184 errmsg("child column \"%s\" specifies generation expression",
3186 errhint("A child table column cannot be generated unless its parent column is.")));
3190 * If new def has a default, override previous default
3192 if (newdef
->raw_default
!= NULL
)
3194 def
->raw_default
= newdef
->raw_default
;
3195 def
->cooked_default
= newdef
->cooked_default
;
3198 /* Mark the column as locally defined */
3199 def
->is_local
= true;
3204 * No, attach new column to result columns
3206 inh_columns
= lappend(inh_columns
, newdef
);
3210 columns
= inh_columns
;
3213 * Check that we haven't exceeded the legal # of columns after merging
3214 * in inherited columns.
3216 if (list_length(columns
) > MaxHeapAttributeNumber
)
3218 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
3219 errmsg("tables can have at most %d columns",
3220 MaxHeapAttributeNumber
)));
3224 * Now that we have the column definition list for a partition, we can
3225 * check whether the columns referenced in the column constraint specs
3226 * actually exist. Also, merge column defaults.
3230 foreach(lc
, saved_columns
)
3232 ColumnDef
*restdef
= lfirst(lc
);
3238 ColumnDef
*coldef
= lfirst(l
);
3240 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
3245 * Check for conflicts related to generated columns.
3247 * Same rules as above: generated-ness has to match the
3248 * parent, but the contents of the generation expression
3251 if (coldef
->generated
)
3253 if (restdef
->raw_default
&& !restdef
->generated
)
3255 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3256 errmsg("column \"%s\" inherits from generated column but specifies default",
3257 restdef
->colname
)));
3258 if (restdef
->identity
)
3260 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3261 errmsg("column \"%s\" inherits from generated column but specifies identity",
3262 restdef
->colname
)));
3266 if (restdef
->generated
)
3268 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3269 errmsg("child column \"%s\" specifies generation expression",
3271 errhint("A child table column cannot be generated unless its parent column is.")));
3275 * Override the parent's default value for this column
3276 * (coldef->cooked_default) with the partition's local
3277 * definition (restdef->raw_default), if there's one. It
3278 * should be physically impossible to get a cooked default
3279 * in the local definition or a raw default in the
3280 * inherited definition, but make sure they're nulls, for
3283 Assert(restdef
->cooked_default
== NULL
);
3284 Assert(coldef
->raw_default
== NULL
);
3285 if (restdef
->raw_default
)
3287 coldef
->raw_default
= restdef
->raw_default
;
3288 coldef
->cooked_default
= NULL
;
3293 /* complain for constraints on columns not in parent */
3296 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3297 errmsg("column \"%s\" does not exist",
3298 restdef
->colname
)));
3303 * If we found any conflicting parent default values, check to make sure
3304 * they were overridden by the child.
3306 if (have_bogus_defaults
)
3308 foreach(lc
, columns
)
3310 ColumnDef
*def
= lfirst(lc
);
3312 if (def
->cooked_default
== &bogus_marker
)
3316 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3317 errmsg("column \"%s\" inherits conflicting generation expressions",
3319 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3322 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3323 errmsg("column \"%s\" inherits conflicting default values",
3325 errhint("To resolve the conflict, specify a default explicitly.")));
3330 *supconstr
= constraints
;
3331 *supnotnulls
= nnconstraints
;
3338 * MergeCheckConstraint
3339 * Try to merge an inherited CHECK constraint with previous ones
3341 * If we inherit identically-named constraints from multiple parents, we must
3342 * merge them, or throw an error if they don't have identical definitions.
3344 * constraints is a list of CookedConstraint structs for previous constraints.
3346 * If the new constraint matches an existing one, then the existing
3347 * constraint's inheritance count is updated. If there is a conflict (same
3348 * name but different expression), throw an error. If the constraint neither
3349 * matches nor conflicts with an existing one, a new constraint is appended to
3353 MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
)
3356 CookedConstraint
*newcon
;
3358 foreach(lc
, constraints
)
3360 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3362 Assert(ccon
->contype
== CONSTR_CHECK
);
3364 /* Non-matching names never conflict */
3365 if (strcmp(ccon
->name
, name
) != 0)
3368 if (equal(expr
, ccon
->expr
))
3370 /* OK to merge constraint with existing */
3372 if (ccon
->inhcount
< 0)
3374 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3375 errmsg("too many inheritance parents"));
3380 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3381 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3386 * Constraint couldn't be merged with an existing one and also didn't
3387 * conflict with an existing one, so add it as a new one to the list.
3389 newcon
= palloc0_object(CookedConstraint
);
3390 newcon
->contype
= CONSTR_CHECK
;
3391 newcon
->name
= pstrdup(name
);
3392 newcon
->expr
= expr
;
3393 newcon
->inhcount
= 1;
3394 return lappend(constraints
, newcon
);
3399 * StoreCatalogInheritance
3400 * Updates the system catalogs with proper inheritance information.
3402 * supers is a list of the OIDs of the new relation's direct ancestors.
3405 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3406 bool child_is_partition
)
3415 Assert(OidIsValid(relationId
));
3421 * Store INHERITS information in pg_inherits using direct ancestors only.
3422 * Also enter dependencies on the direct ancestors, and make sure they are
3423 * marked with relhassubclass = true.
3425 * (Once upon a time, both direct and indirect ancestors were found here
3426 * and then entered into pg_ipl. Since that catalog doesn't exist
3427 * anymore, there's no need to look for indirect ancestors.)
3429 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3432 foreach(entry
, supers
)
3434 Oid parentOid
= lfirst_oid(entry
);
3436 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3437 child_is_partition
);
3441 table_close(relation
, RowExclusiveLock
);
3445 * Make catalog entries showing relationId as being an inheritance child
3446 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3449 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3450 int32 seqNumber
, Relation inhRelation
,
3451 bool child_is_partition
)
3453 ObjectAddress childobject
,
3456 /* store the pg_inherits row */
3457 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3460 * Store a dependency too
3462 parentobject
.classId
= RelationRelationId
;
3463 parentobject
.objectId
= parentOid
;
3464 parentobject
.objectSubId
= 0;
3465 childobject
.classId
= RelationRelationId
;
3466 childobject
.objectId
= relationId
;
3467 childobject
.objectSubId
= 0;
3469 recordDependencyOn(&childobject
, &parentobject
,
3470 child_dependency_type(child_is_partition
));
3473 * Post creation hook of this inheritance. Since object_access_hook
3474 * doesn't take multiple object identifiers, we relay oid of parent
3475 * relation using auxiliary_id argument.
3477 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3482 * Mark the parent as having subclasses.
3484 SetRelationHasSubclass(parentOid
, true);
3488 * Look for an existing column entry with the given name.
3490 * Returns the index (starting with 1) if attribute already exists in columns,
3494 findAttrByName(const char *attributeName
, const List
*columns
)
3499 foreach(lc
, columns
)
3501 if (strcmp(attributeName
, lfirst_node(ColumnDef
, lc
)->colname
) == 0)
3511 * SetRelationHasSubclass
3512 * Set the value of the relation's relhassubclass field in pg_class.
3514 * NOTE: caller must be holding an appropriate lock on the relation.
3515 * ShareUpdateExclusiveLock is sufficient.
3517 * NOTE: an important side-effect of this operation is that an SI invalidation
3518 * message is sent out to all backends --- including me --- causing plans
3519 * referencing the relation to be rebuilt with the new list of children.
3520 * This must happen even if we find that no change is needed in the pg_class
3524 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3526 Relation relationRelation
;
3528 Form_pg_class classtuple
;
3531 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3533 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3534 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3535 if (!HeapTupleIsValid(tuple
))
3536 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3537 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3539 if (classtuple
->relhassubclass
!= relhassubclass
)
3541 classtuple
->relhassubclass
= relhassubclass
;
3542 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3546 /* no need to change tuple, but force relcache rebuild anyway */
3547 CacheInvalidateRelcacheByTuple(tuple
);
3550 heap_freetuple(tuple
);
3551 table_close(relationRelation
, RowExclusiveLock
);
3555 * CheckRelationTableSpaceMove
3556 * Check if relation can be moved to new tablespace.
3558 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3560 * Returns true if the relation can be moved to the new tablespace; raises
3561 * an error if it is not possible to do the move; returns false if the move
3562 * would have no effect.
3565 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3567 Oid oldTableSpaceId
;
3570 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3573 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3574 if (newTableSpaceId
== oldTableSpaceId
||
3575 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3579 * We cannot support moving mapped relations into different tablespaces.
3580 * (In particular this eliminates all shared catalogs.)
3582 if (RelationIsMapped(rel
))
3584 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3585 errmsg("cannot move system relation \"%s\"",
3586 RelationGetRelationName(rel
))));
3588 /* Cannot move a non-shared relation into pg_global */
3589 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3591 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3592 errmsg("only shared relations can be placed in pg_global tablespace")));
3595 * Do not allow moving temp tables of other backends ... their local
3596 * buffer manager is not going to cope.
3598 if (RELATION_IS_OTHER_TEMP(rel
))
3600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3601 errmsg("cannot move temporary tables of other sessions")));
3607 * SetRelationTableSpace
3608 * Set new reltablespace and relfilenumber in pg_class entry.
3610 * newTableSpaceId is the new tablespace for the relation, and
3611 * newRelFilenumber its new filenumber. If newRelFilenumber is
3612 * InvalidRelFileNumber, this field is not updated.
3614 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3616 * The caller of this routine had better check if a relation can be
3617 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3618 * first, and is responsible for making the change visible with
3619 * CommandCounterIncrement().
3622 SetRelationTableSpace(Relation rel
,
3623 Oid newTableSpaceId
,
3624 RelFileNumber newRelFilenumber
)
3628 Form_pg_class rd_rel
;
3629 Oid reloid
= RelationGetRelid(rel
);
3631 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3633 /* Get a modifiable copy of the relation's pg_class row. */
3634 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3636 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3637 if (!HeapTupleIsValid(tuple
))
3638 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3639 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3641 /* Update the pg_class row. */
3642 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3643 InvalidOid
: newTableSpaceId
;
3644 if (RelFileNumberIsValid(newRelFilenumber
))
3645 rd_rel
->relfilenode
= newRelFilenumber
;
3646 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
3649 * Record dependency on tablespace. This is only required for relations
3650 * that have no physical storage.
3652 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3653 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3654 rd_rel
->reltablespace
);
3656 heap_freetuple(tuple
);
3657 table_close(pg_class
, RowExclusiveLock
);
3661 * renameatt_check - basic sanity checks before attribute rename
3664 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3666 char relkind
= classform
->relkind
;
3668 if (classform
->reloftype
&& !recursing
)
3670 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3671 errmsg("cannot rename column of typed table")));
3674 * Renaming the columns of sequences or toast tables doesn't actually
3675 * break anything from the system's point of view, since internal
3676 * references are by attnum. But it doesn't seem right to allow users to
3677 * change names that are hardcoded into the system, hence the following
3680 if (relkind
!= RELKIND_RELATION
&&
3681 relkind
!= RELKIND_VIEW
&&
3682 relkind
!= RELKIND_MATVIEW
&&
3683 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3684 relkind
!= RELKIND_INDEX
&&
3685 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3686 relkind
!= RELKIND_FOREIGN_TABLE
&&
3687 relkind
!= RELKIND_PARTITIONED_TABLE
)
3689 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3690 errmsg("cannot rename columns of relation \"%s\"",
3691 NameStr(classform
->relname
)),
3692 errdetail_relkind_not_supported(relkind
)));
3695 * permissions checking. only the owner of a class can change its schema.
3697 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3698 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3699 NameStr(classform
->relname
));
3700 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3702 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3703 errmsg("permission denied: \"%s\" is a system catalog",
3704 NameStr(classform
->relname
))));
3708 * renameatt_internal - workhorse for renameatt
3710 * Return value is the attribute number in the 'myrelid' relation.
3713 renameatt_internal(Oid myrelid
,
3714 const char *oldattname
,
3715 const char *newattname
,
3718 int expected_parents
,
3719 DropBehavior behavior
)
3721 Relation targetrelation
;
3722 Relation attrelation
;
3724 Form_pg_attribute attform
;
3728 * Grab an exclusive lock on the target table, which we will NOT release
3729 * until end of transaction.
3731 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3732 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3735 * if the 'recurse' flag is set then we are supposed to rename this
3736 * attribute in all classes that inherit from 'relname' (as well as in
3739 * any permissions or problems with duplicate attributes will cause the
3740 * whole transaction to abort, which is what we want -- all or nothing.
3750 * we need the number of parents for each child so that the recursive
3751 * calls to renameatt() can determine whether there are any parents
3752 * outside the inheritance hierarchy being processed.
3754 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3758 * find_all_inheritors does the recursive search of the inheritance
3759 * hierarchy, so all we have to do is process all of the relids in the
3760 * list that it returns.
3762 forboth(lo
, child_oids
, li
, child_numparents
)
3764 Oid childrelid
= lfirst_oid(lo
);
3765 int numparents
= lfirst_int(li
);
3767 if (childrelid
== myrelid
)
3769 /* note we need not recurse again */
3770 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3776 * If we are told not to recurse, there had better not be any child
3777 * tables; else the rename would put them out of step.
3779 * expected_parents will only be 0 if we are not already recursing.
3781 if (expected_parents
== 0 &&
3782 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3784 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3785 errmsg("inherited column \"%s\" must be renamed in child tables too",
3789 /* rename attributes in typed tables of composite type */
3790 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3795 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3796 RelationGetRelationName(targetrelation
),
3799 foreach(lo
, child_oids
)
3800 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3803 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3805 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3806 if (!HeapTupleIsValid(atttup
))
3808 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3809 errmsg("column \"%s\" does not exist",
3811 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3813 attnum
= attform
->attnum
;
3816 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3817 errmsg("cannot rename system column \"%s\"",
3821 * if the attribute is inherited, forbid the renaming. if this is a
3822 * top-level call to renameatt(), then expected_parents will be 0, so the
3823 * effect of this code will be to prohibit the renaming if the attribute
3824 * is inherited at all. if this is a recursive call to renameatt(),
3825 * expected_parents will be the number of parents the current relation has
3826 * within the inheritance hierarchy being processed, so we'll prohibit the
3827 * renaming only if there are additional parents from elsewhere.
3829 if (attform
->attinhcount
> expected_parents
)
3831 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3832 errmsg("cannot rename inherited column \"%s\"",
3835 /* new name should not already exist */
3836 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3838 /* apply the update */
3839 namestrcpy(&(attform
->attname
), newattname
);
3841 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3843 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3845 heap_freetuple(atttup
);
3847 table_close(attrelation
, RowExclusiveLock
);
3849 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3855 * Perform permissions and integrity checks before acquiring a relation lock.
3858 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3864 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3865 if (!HeapTupleIsValid(tuple
))
3866 return; /* concurrently dropped */
3867 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3868 renameatt_check(relid
, form
, false);
3869 ReleaseSysCache(tuple
);
3873 * renameatt - changes the name of an attribute in a relation
3875 * The returned ObjectAddress is that of the renamed column.
3878 renameatt(RenameStmt
*stmt
)
3882 ObjectAddress address
;
3884 /* lock level taken here should match renameatt_internal */
3885 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3886 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3887 RangeVarCallbackForRenameAttribute
,
3890 if (!OidIsValid(relid
))
3893 (errmsg("relation \"%s\" does not exist, skipping",
3894 stmt
->relation
->relname
)));
3895 return InvalidObjectAddress
;
3899 renameatt_internal(relid
,
3900 stmt
->subname
, /* old att name */
3901 stmt
->newname
, /* new att name */
3902 stmt
->relation
->inh
, /* recursive? */
3903 false, /* recursing? */
3904 0, /* expected inhcount */
3907 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3913 * same logic as renameatt_internal
3915 static ObjectAddress
3916 rename_constraint_internal(Oid myrelid
,
3918 const char *oldconname
,
3919 const char *newconname
,
3922 int expected_parents
)
3924 Relation targetrelation
= NULL
;
3927 Form_pg_constraint con
;
3928 ObjectAddress address
;
3930 Assert(!myrelid
|| !mytypid
);
3934 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
3938 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3941 * don't tell it whether we're recursing; we allow changing typed
3944 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
3946 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
3949 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
3950 if (!HeapTupleIsValid(tuple
))
3951 elog(ERROR
, "cache lookup failed for constraint %u",
3953 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
3956 (con
->contype
== CONSTRAINT_CHECK
||
3957 con
->contype
== CONSTRAINT_NOTNULL
) &&
3967 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3970 forboth(lo
, child_oids
, li
, child_numparents
)
3972 Oid childrelid
= lfirst_oid(lo
);
3973 int numparents
= lfirst_int(li
);
3975 if (childrelid
== myrelid
)
3978 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
3983 if (expected_parents
== 0 &&
3984 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3986 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3987 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3991 if (con
->coninhcount
> expected_parents
)
3993 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3994 errmsg("cannot rename inherited constraint \"%s\"",
3999 && (con
->contype
== CONSTRAINT_PRIMARY
4000 || con
->contype
== CONSTRAINT_UNIQUE
4001 || con
->contype
== CONSTRAINT_EXCLUSION
))
4002 /* rename the index; this renames the constraint as well */
4003 RenameRelationInternal(con
->conindid
, newconname
, false, true);
4005 RenameConstraintById(constraintOid
, newconname
);
4007 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
4009 ReleaseSysCache(tuple
);
4014 * Invalidate relcache so as others can see the new constraint name.
4016 CacheInvalidateRelcache(targetrelation
);
4018 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
4025 RenameConstraint(RenameStmt
*stmt
)
4027 Oid relid
= InvalidOid
;
4028 Oid typid
= InvalidOid
;
4030 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
4035 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
4036 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
4037 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
4038 if (!HeapTupleIsValid(tup
))
4039 elog(ERROR
, "cache lookup failed for type %u", typid
);
4040 checkDomainOwner(tup
);
4041 ReleaseSysCache(tup
);
4042 table_close(rel
, NoLock
);
4046 /* lock level taken here should match rename_constraint_internal */
4047 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
4048 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4049 RangeVarCallbackForRenameAttribute
,
4051 if (!OidIsValid(relid
))
4054 (errmsg("relation \"%s\" does not exist, skipping",
4055 stmt
->relation
->relname
)));
4056 return InvalidObjectAddress
;
4061 rename_constraint_internal(relid
, typid
,
4065 stmt
->relation
->inh
), /* recursive? */
4066 false, /* recursing? */
4067 0 /* expected inhcount */ );
4071 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4075 RenameRelation(RenameStmt
*stmt
)
4077 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
4079 ObjectAddress address
;
4082 * Grab an exclusive lock on the target table, index, sequence, view,
4083 * materialized view, or foreign table, which we will NOT release until
4084 * end of transaction.
4086 * Lock level used here should match RenameRelationInternal, to avoid lock
4087 * escalation. However, because ALTER INDEX can be used with any relation
4088 * type, we mustn't believe without verification.
4096 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
4098 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4099 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4100 RangeVarCallbackForAlterRelation
,
4103 if (!OidIsValid(relid
))
4106 (errmsg("relation \"%s\" does not exist, skipping",
4107 stmt
->relation
->relname
)));
4108 return InvalidObjectAddress
;
4112 * We allow mismatched statement and object types (e.g., ALTER INDEX
4113 * to rename a table), but we might've used the wrong lock level. If
4114 * that happens, retry with the correct lock level. We don't bother
4115 * if we already acquired AccessExclusiveLock with an index, however.
4117 relkind
= get_rel_relkind(relid
);
4118 obj_is_index
= (relkind
== RELKIND_INDEX
||
4119 relkind
== RELKIND_PARTITIONED_INDEX
);
4120 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
4123 UnlockRelationOid(relid
, lockmode
);
4124 is_index_stmt
= obj_is_index
;
4128 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
4130 ObjectAddressSet(address
, RelationRelationId
, relid
);
4136 * RenameRelationInternal - change the name of a relation
4139 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
4141 Relation targetrelation
;
4142 Relation relrelation
; /* for RELATION relation */
4144 Form_pg_class relform
;
4148 * Grab a lock on the target relation, which we will NOT release until end
4149 * of transaction. We need at least a self-exclusive lock so that
4150 * concurrent DDL doesn't overwrite the rename if they start updating
4151 * while still seeing the old version. The lock also guards against
4152 * triggering relcache reloads in concurrent sessions, which might not
4153 * handle this information changing under them. For indexes, we can use a
4154 * reduced lock level because RelationReloadIndexInfo() handles indexes
4157 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
4158 namespaceId
= RelationGetNamespace(targetrelation
);
4161 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4163 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4165 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4166 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4167 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4168 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4170 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
4172 (errcode(ERRCODE_DUPLICATE_TABLE
),
4173 errmsg("relation \"%s\" already exists",
4177 * RenameRelation is careful not to believe the caller's idea of the
4178 * relation kind being handled. We don't have to worry about this, but
4179 * let's not be totally oblivious to it. We can process an index as
4180 * not-an-index, but not the other way around.
4183 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4184 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4187 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4188 * because it's a copy...)
4190 namestrcpy(&(relform
->relname
), newrelname
);
4192 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4194 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4195 InvalidOid
, is_internal
);
4197 heap_freetuple(reltup
);
4198 table_close(relrelation
, RowExclusiveLock
);
4201 * Also rename the associated type, if any.
4203 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4204 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4205 newrelname
, namespaceId
);
4208 * Also rename the associated constraint, if any.
4210 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4211 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4213 Oid constraintId
= get_index_constraint(myrelid
);
4215 if (OidIsValid(constraintId
))
4216 RenameConstraintById(constraintId
, newrelname
);
4220 * Close rel, but keep lock!
4222 relation_close(targetrelation
, NoLock
);
4226 * ResetRelRewrite - reset relrewrite
4229 ResetRelRewrite(Oid myrelid
)
4231 Relation relrelation
; /* for RELATION relation */
4233 Form_pg_class relform
;
4236 * Find relation's pg_class tuple.
4238 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4240 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4241 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4242 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4243 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4246 * Update pg_class tuple.
4248 relform
->relrewrite
= InvalidOid
;
4250 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4252 heap_freetuple(reltup
);
4253 table_close(relrelation
, RowExclusiveLock
);
4257 * Disallow ALTER TABLE (and similar commands) when the current backend has
4258 * any open reference to the target table besides the one just acquired by
4259 * the calling command; this implies there's an open cursor or active plan.
4260 * We need this check because our lock doesn't protect us against stomping
4261 * on our own foot, only other people's feet!
4263 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4264 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4265 * possibly be relaxed to only error out for certain types of alterations.
4266 * But the use-case for allowing any of these things is not obvious, so we
4267 * won't work hard at it for now.
4269 * We also reject these commands if there are any pending AFTER trigger events
4270 * for the rel. This is certainly necessary for the rewriting variants of
4271 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4272 * events would try to fetch the wrong tuples. It might be overly cautious
4273 * in other cases, but again it seems better to err on the side of paranoia.
4275 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4276 * we are worried about active indexscans on the index. The trigger-event
4277 * check can be skipped, since we are doing no damage to the parent table.
4279 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4282 CheckTableNotInUse(Relation rel
, const char *stmt
)
4284 int expected_refcnt
;
4286 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4287 if (rel
->rd_refcnt
!= expected_refcnt
)
4289 (errcode(ERRCODE_OBJECT_IN_USE
),
4290 /* translator: first %s is a SQL command, eg ALTER TABLE */
4291 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4292 stmt
, RelationGetRelationName(rel
))));
4294 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4295 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4296 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4298 (errcode(ERRCODE_OBJECT_IN_USE
),
4299 /* translator: first %s is a SQL command, eg ALTER TABLE */
4300 errmsg("cannot %s \"%s\" because it has pending trigger events",
4301 stmt
, RelationGetRelationName(rel
))));
4305 * AlterTableLookupRelation
4306 * Look up, and lock, the OID for the relation named by an alter table
4310 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4312 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4313 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4314 RangeVarCallbackForAlterRelation
,
4320 * Execute ALTER TABLE, which can be a list of subcommands
4322 * ALTER TABLE is performed in three phases:
4323 * 1. Examine subcommands and perform pre-transformation checking.
4324 * 2. Validate and transform subcommands, and update system catalogs.
4325 * 3. Scan table(s) to check new constraints, and optionally recopy
4326 * the data into new table(s).
4327 * Phase 3 is not performed unless one or more of the subcommands requires
4328 * it. The intention of this design is to allow multiple independent
4329 * updates of the table schema to be performed with only one pass over the
4332 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4333 * each table to be affected (there may be multiple affected tables if the
4334 * commands traverse a table inheritance hierarchy). Also we do preliminary
4335 * validation of the subcommands. Because earlier subcommands may change
4336 * the catalog state seen by later commands, there are limits to what can
4337 * be done in this phase. Generally, this phase acquires table locks,
4338 * checks permissions and relkind, and recurses to find child tables.
4340 * ATRewriteCatalogs performs phase 2 for each affected table.
4341 * Certain subcommands need to be performed before others to avoid
4342 * unnecessary conflicts; for example, DROP COLUMN should come before
4343 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4344 * lists, one for each logical "pass" of phase 2.
4346 * ATRewriteTables performs phase 3 for those tables that need it.
4348 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4349 * since phase 1 already does it. However, for certain subcommand types
4350 * it is only possible to determine how to recurse at phase 2 time; for
4351 * those cases, phase 1 sets the cmd->recurse flag.
4353 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4354 * the whole operation; we don't have to do anything special to clean up.
4356 * The caller must lock the relation, with an appropriate lock level
4357 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4358 * or higher. We pass the lock level down
4359 * so that we can apply it recursively to inherited tables. Note that the
4360 * lock level we want as we recurse might well be higher than required for
4361 * that specific subcommand. So we pass down the overall lock requirement,
4362 * rather than reassess it at lower levels.
4364 * The caller also provides a "context" which is to be passed back to
4365 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4366 * Some of the fields therein, such as the relid, are used here as well.
4369 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4370 AlterTableUtilityContext
*context
)
4374 /* Caller is required to provide an adequate lock. */
4375 rel
= relation_open(context
->relid
, NoLock
);
4377 CheckTableNotInUse(rel
, "ALTER TABLE");
4379 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4383 * AlterTableInternal
4385 * ALTER TABLE with target specified by OID
4387 * We do not reject if the relation is already open, because it's quite
4388 * likely that one or more layers of caller have it open. That means it
4389 * is unsafe to use this entry point for alterations that could break
4390 * existing query plans. On the assumption it's not used for such, we
4391 * don't have to reject pending AFTER triggers, either.
4393 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4394 * used for any subcommand types that require parse transformation or
4395 * could generate subcommands that have to be passed to ProcessUtility.
4398 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4401 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4403 rel
= relation_open(relid
, lockmode
);
4405 EventTriggerAlterTableRelid(relid
);
4407 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4411 * AlterTableGetLockLevel
4413 * Sets the overall lock level required for the supplied list of subcommands.
4414 * Policy for doing this set according to needs of AlterTable(), see
4415 * comments there for overall explanation.
4417 * Function is called before and after parsing, so it must give same
4418 * answer each time it is called. Some subcommands are transformed
4419 * into other subcommand types, so the transform must never be made to a
4420 * lower lock level than previously assigned. All transforms are noted below.
4422 * Since this is called before we lock the table we cannot use table metadata
4423 * to influence the type of lock we acquire.
4425 * There should be no lockmodes hardcoded into the subcommand functions. All
4426 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4427 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4428 * and does not travel through this section of code and cannot be combined with
4429 * any of the subcommands given here.
4431 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4432 * so any changes that might affect SELECTs running on standbys need to use
4433 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4434 * have a solution for that also.
4436 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4437 * that takes a lock less than AccessExclusiveLock can change object definitions
4438 * while pg_dump is running. Be careful to check that the appropriate data is
4439 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4440 * otherwise we might end up with an inconsistent dump that can't restore.
4443 AlterTableGetLockLevel(List
*cmds
)
4446 * This only works if we read catalog tables using MVCC snapshots.
4449 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4453 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4454 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4456 switch (cmd
->subtype
)
4459 * These subcommands rewrite the heap, so require full locks.
4461 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4463 case AT_SetAccessMethod
: /* must rewrite heap */
4464 case AT_SetTableSpace
: /* must rewrite heap */
4465 case AT_AlterColumnType
: /* must rewrite heap */
4466 cmd_lockmode
= AccessExclusiveLock
;
4470 * These subcommands may require addition of toast tables. If
4471 * we add a toast table to a table currently being scanned, we
4472 * might miss data added to the new toast table by concurrent
4473 * insert transactions.
4475 case AT_SetStorage
: /* may add toast tables, see
4476 * ATRewriteCatalogs() */
4477 cmd_lockmode
= AccessExclusiveLock
;
4481 * Removing constraints can affect SELECTs that have been
4482 * optimized assuming the constraint holds true. See also
4483 * CloneFkReferenced.
4485 case AT_DropConstraint
: /* as DROP INDEX */
4486 case AT_DropNotNull
: /* may change some SQL plans */
4487 cmd_lockmode
= AccessExclusiveLock
;
4491 * Subcommands that may be visible to concurrent SELECTs
4493 case AT_DropColumn
: /* change visible to SELECT */
4494 case AT_AddColumnToView
: /* CREATE VIEW */
4495 case AT_DropOids
: /* used to equiv to DropColumn */
4496 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4497 case AT_EnableReplicaRule
: /* may change SELECT rules */
4498 case AT_EnableRule
: /* may change SELECT rules */
4499 case AT_DisableRule
: /* may change SELECT rules */
4500 cmd_lockmode
= AccessExclusiveLock
;
4504 * Changing owner may remove implicit SELECT privileges
4506 case AT_ChangeOwner
: /* change visible to SELECT */
4507 cmd_lockmode
= AccessExclusiveLock
;
4511 * Changing foreign table options may affect optimization.
4513 case AT_GenericOptions
:
4514 case AT_AlterColumnGenericOptions
:
4515 cmd_lockmode
= AccessExclusiveLock
;
4519 * These subcommands affect write operations only.
4522 case AT_EnableAlwaysTrig
:
4523 case AT_EnableReplicaTrig
:
4524 case AT_EnableTrigAll
:
4525 case AT_EnableTrigUser
:
4526 case AT_DisableTrig
:
4527 case AT_DisableTrigAll
:
4528 case AT_DisableTrigUser
:
4529 cmd_lockmode
= ShareRowExclusiveLock
;
4533 * These subcommands affect write operations only. XXX
4534 * Theoretically, these could be ShareRowExclusiveLock.
4536 case AT_ColumnDefault
:
4537 case AT_CookedColumnDefault
:
4538 case AT_AlterConstraint
:
4539 case AT_AddIndex
: /* from ADD CONSTRAINT */
4540 case AT_AddIndexConstraint
:
4541 case AT_ReplicaIdentity
:
4543 case AT_SetAttNotNull
:
4544 case AT_EnableRowSecurity
:
4545 case AT_DisableRowSecurity
:
4546 case AT_ForceRowSecurity
:
4547 case AT_NoForceRowSecurity
:
4548 case AT_AddIdentity
:
4549 case AT_DropIdentity
:
4550 case AT_SetIdentity
:
4551 case AT_DropExpression
:
4552 case AT_SetCompression
:
4553 cmd_lockmode
= AccessExclusiveLock
;
4556 case AT_AddConstraint
:
4557 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4558 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4559 if (IsA(cmd
->def
, Constraint
))
4561 Constraint
*con
= (Constraint
*) cmd
->def
;
4563 switch (con
->contype
)
4565 case CONSTR_EXCLUSION
:
4566 case CONSTR_PRIMARY
:
4570 * Cases essentially the same as CREATE INDEX. We
4571 * could reduce the lock strength to ShareLock if
4572 * we can work out how to allow concurrent catalog
4573 * updates. XXX Might be set down to
4574 * ShareRowExclusiveLock but requires further
4577 cmd_lockmode
= AccessExclusiveLock
;
4579 case CONSTR_FOREIGN
:
4582 * We add triggers to both tables when we add a
4583 * Foreign Key, so the lock level must be at least
4584 * as strong as CREATE TRIGGER.
4586 cmd_lockmode
= ShareRowExclusiveLock
;
4590 cmd_lockmode
= AccessExclusiveLock
;
4596 * These subcommands affect inheritance behaviour. Queries
4597 * started before us will continue to see the old inheritance
4598 * behaviour, while queries started after we commit will see
4599 * new behaviour. No need to prevent reads or writes to the
4600 * subtable while we hook it up though. Changing the TupDesc
4601 * may be a problem, so keep highest lock.
4604 case AT_DropInherit
:
4605 cmd_lockmode
= AccessExclusiveLock
;
4609 * These subcommands affect implicit row type conversion. They
4610 * have affects similar to CREATE/DROP CAST on queries. don't
4611 * provide for invalidating parse trees as a result of such
4612 * changes, so we keep these at AccessExclusiveLock.
4616 cmd_lockmode
= AccessExclusiveLock
;
4620 * Only used by CREATE OR REPLACE VIEW which must conflict
4621 * with an SELECTs currently using the view.
4623 case AT_ReplaceRelOptions
:
4624 cmd_lockmode
= AccessExclusiveLock
;
4628 * These subcommands affect general strategies for performance
4629 * and maintenance, though don't change the semantic results
4630 * from normal data reads and writes. Delaying an ALTER TABLE
4631 * behind currently active writes only delays the point where
4632 * the new strategy begins to take effect, so there is no
4633 * benefit in waiting. In this case the minimum restriction
4634 * applies: we don't currently allow concurrent catalog
4637 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4638 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4639 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4640 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4641 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4642 cmd_lockmode
= ShareUpdateExclusiveLock
;
4646 case AT_SetUnLogged
:
4647 cmd_lockmode
= AccessExclusiveLock
;
4650 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4651 cmd_lockmode
= ShareUpdateExclusiveLock
;
4655 * Rel options are more complex than first appears. Options
4656 * are set here for tables, views and indexes; for historical
4657 * reasons these can all be used with ALTER TABLE, so we can't
4658 * decide between them using the basic grammar.
4660 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4662 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4664 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4667 case AT_AttachPartition
:
4668 cmd_lockmode
= ShareUpdateExclusiveLock
;
4671 case AT_DetachPartition
:
4672 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4673 cmd_lockmode
= ShareUpdateExclusiveLock
;
4675 cmd_lockmode
= AccessExclusiveLock
;
4678 case AT_DetachPartitionFinalize
:
4679 cmd_lockmode
= ShareUpdateExclusiveLock
;
4683 elog(ERROR
, "unrecognized alter table type: %d",
4684 (int) cmd
->subtype
);
4689 * Take the greatest lockmode from any subcommand
4691 if (cmd_lockmode
> lockmode
)
4692 lockmode
= cmd_lockmode
;
4699 * ATController provides top level control over the phases.
4701 * parsetree is passed in to allow it to be passed to event triggers
4705 ATController(AlterTableStmt
*parsetree
,
4706 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4707 AlterTableUtilityContext
*context
)
4712 /* Phase 1: preliminary examination of commands, create work queue */
4715 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4717 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4720 /* Close the relation, but keep lock until commit */
4721 relation_close(rel
, NoLock
);
4723 /* Phase 2: update system catalogs */
4724 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4726 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4727 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4733 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4734 * recursion and permission checks.
4736 * Caller must have acquired appropriate lock type on relation already.
4737 * This lock should be held until commit.
4740 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4741 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4742 AlterTableUtilityContext
*context
)
4744 AlteredTableInfo
*tab
;
4745 int pass
= AT_PASS_UNSET
;
4747 /* Find or create work queue entry for this table */
4748 tab
= ATGetQueueEntry(wqueue
, rel
);
4751 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4752 * partitions that are pending detach.
4754 if (rel
->rd_rel
->relispartition
&&
4755 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4756 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4758 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4759 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4760 RelationGetRelationName(rel
)),
4761 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4764 * Copy the original subcommand for each table, so we can scribble on it.
4765 * This avoids conflicts when different child tables need to make
4766 * different parse transformations (for example, the same column may have
4767 * different column numbers in different children).
4769 cmd
= copyObject(cmd
);
4772 * Do permissions and relkind checking, recursion to child tables if
4773 * needed, and any additional phase-1 processing needed. (But beware of
4774 * adding any processing that looks at table details that another
4775 * subcommand could change. In some cases we reject multiple subcommands
4776 * that could try to change the same state in contrary ways.)
4778 switch (cmd
->subtype
)
4780 case AT_AddColumn
: /* ADD COLUMN */
4781 ATSimplePermissions(cmd
->subtype
, rel
,
4782 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4783 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4785 /* Recursion occurs during execution phase */
4786 pass
= AT_PASS_ADD_COL
;
4788 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4789 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4790 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4792 /* Recursion occurs during execution phase */
4793 pass
= AT_PASS_ADD_COL
;
4795 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4798 * We allow defaults on views so that INSERT into a view can have
4799 * default-ish behavior. This works because the rewriter
4800 * substitutes default values into INSERTs before it expands
4803 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4804 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4805 /* No command-specific prep needed */
4806 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4808 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4809 /* This is currently used only in CREATE TABLE */
4810 /* (so the permission check really isn't necessary) */
4811 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4812 /* This command never recurses */
4813 pass
= AT_PASS_ADD_OTHERCONSTR
;
4815 case AT_AddIdentity
:
4816 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4817 /* This command never recurses */
4818 pass
= AT_PASS_ADD_OTHERCONSTR
;
4820 case AT_SetIdentity
:
4821 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4822 /* This command never recurses */
4823 /* This should run after AddIdentity, so do it in MISC pass */
4824 pass
= AT_PASS_MISC
;
4826 case AT_DropIdentity
:
4827 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4828 /* This command never recurses */
4829 pass
= AT_PASS_DROP
;
4831 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4832 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4833 /* Set up recursion for phase 2; no other prep needed */
4835 cmd
->recurse
= true;
4836 pass
= AT_PASS_DROP
;
4838 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4839 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4840 /* Set up recursion for phase 2; no other prep needed */
4842 cmd
->recurse
= true;
4843 pass
= AT_PASS_COL_ATTRS
;
4845 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull without adding
4847 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4848 /* Need command-specific recursion decision */
4849 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4850 pass
= AT_PASS_COL_ATTRS
;
4852 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4853 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4854 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4855 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4856 pass
= AT_PASS_DROP
;
4858 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4859 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4860 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4861 /* No command-specific prep needed */
4862 pass
= AT_PASS_MISC
;
4864 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4865 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4866 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4867 /* This command never recurses */
4868 pass
= AT_PASS_MISC
;
4870 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4871 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4872 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4873 /* No command-specific prep needed */
4874 pass
= AT_PASS_MISC
;
4876 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4877 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4878 /* This command never recurses */
4879 /* No command-specific prep needed */
4880 pass
= AT_PASS_MISC
;
4882 case AT_DropColumn
: /* DROP COLUMN */
4883 ATSimplePermissions(cmd
->subtype
, rel
,
4884 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4885 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
4887 /* Recursion occurs during execution phase */
4888 pass
= AT_PASS_DROP
;
4890 case AT_AddIndex
: /* ADD INDEX */
4891 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4892 /* This command never recurses */
4893 /* No command-specific prep needed */
4894 pass
= AT_PASS_ADD_INDEX
;
4896 case AT_AddConstraint
: /* ADD CONSTRAINT */
4897 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4898 /* Recursion occurs during execution phase */
4899 /* No command-specific prep needed except saving recurse flag */
4901 cmd
->recurse
= true;
4902 pass
= AT_PASS_ADD_CONSTR
;
4904 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
4905 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4906 /* This command never recurses */
4907 /* No command-specific prep needed */
4908 pass
= AT_PASS_ADD_INDEXCONSTR
;
4910 case AT_DropConstraint
: /* DROP CONSTRAINT */
4911 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4912 ATCheckPartitionsNotInUse(rel
, lockmode
);
4913 /* Other recursion occurs during execution phase */
4914 /* No command-specific prep needed except saving recurse flag */
4916 cmd
->recurse
= true;
4917 pass
= AT_PASS_DROP
;
4919 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
4920 ATSimplePermissions(cmd
->subtype
, rel
,
4921 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4922 /* See comments for ATPrepAlterColumnType */
4923 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
4924 AT_PASS_UNSET
, context
);
4925 Assert(cmd
!= NULL
);
4926 /* Performs own recursion */
4927 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
4929 pass
= AT_PASS_ALTER_TYPE
;
4931 case AT_AlterColumnGenericOptions
:
4932 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
4933 /* This command never recurses */
4934 /* No command-specific prep needed */
4935 pass
= AT_PASS_MISC
;
4937 case AT_ChangeOwner
: /* ALTER OWNER */
4938 /* This command never recurses */
4939 /* No command-specific prep needed */
4940 pass
= AT_PASS_MISC
;
4942 case AT_ClusterOn
: /* CLUSTER ON */
4943 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
4944 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4945 /* These commands never recurse */
4946 /* No command-specific prep needed */
4947 pass
= AT_PASS_MISC
;
4949 case AT_SetLogged
: /* SET LOGGED */
4950 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4951 if (tab
->chgPersistence
)
4953 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4954 errmsg("cannot change persistence setting twice")));
4955 tab
->chgPersistence
= ATPrepChangePersistence(rel
, true);
4956 /* force rewrite if necessary; see comment in ATRewriteTables */
4957 if (tab
->chgPersistence
)
4959 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4960 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
4962 pass
= AT_PASS_MISC
;
4964 case AT_SetUnLogged
: /* SET UNLOGGED */
4965 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4966 if (tab
->chgPersistence
)
4968 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4969 errmsg("cannot change persistence setting twice")));
4970 tab
->chgPersistence
= ATPrepChangePersistence(rel
, false);
4971 /* force rewrite if necessary; see comment in ATRewriteTables */
4972 if (tab
->chgPersistence
)
4974 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4975 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
4977 pass
= AT_PASS_MISC
;
4979 case AT_DropOids
: /* SET WITHOUT OIDS */
4980 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4981 pass
= AT_PASS_DROP
;
4983 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
4984 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4986 /* partitioned tables don't have an access method */
4987 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
4989 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
4990 errmsg("cannot change access method of a partitioned table")));
4992 /* check if another access method change was already requested */
4993 if (OidIsValid(tab
->newAccessMethod
))
4995 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4996 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
4998 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
4999 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
5001 case AT_SetTableSpace
: /* SET TABLESPACE */
5002 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
|
5003 ATT_PARTITIONED_INDEX
);
5004 /* This command never recurses */
5005 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
5006 pass
= AT_PASS_MISC
; /* doesn't actually matter */
5008 case AT_SetRelOptions
: /* SET (...) */
5009 case AT_ResetRelOptions
: /* RESET (...) */
5010 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
5011 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_MATVIEW
| ATT_INDEX
);
5012 /* This command never recurses */
5013 /* No command-specific prep needed */
5014 pass
= AT_PASS_MISC
;
5016 case AT_AddInherit
: /* INHERIT */
5017 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5018 /* This command never recurses */
5019 ATPrepAddInherit(rel
);
5020 pass
= AT_PASS_MISC
;
5022 case AT_DropInherit
: /* NO INHERIT */
5023 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5024 /* This command never recurses */
5025 /* No command-specific prep needed */
5026 pass
= AT_PASS_MISC
;
5028 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5029 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5030 /* Recursion occurs during execution phase */
5031 pass
= AT_PASS_MISC
;
5033 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5034 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5035 /* Recursion occurs during execution phase */
5036 /* No command-specific prep needed except saving recurse flag */
5038 cmd
->recurse
= true;
5039 pass
= AT_PASS_MISC
;
5041 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
5042 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5043 pass
= AT_PASS_MISC
;
5044 /* This command never recurses */
5045 /* No command-specific prep needed */
5047 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
5048 case AT_EnableAlwaysTrig
:
5049 case AT_EnableReplicaTrig
:
5050 case AT_EnableTrigAll
:
5051 case AT_EnableTrigUser
:
5052 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
5053 case AT_DisableTrigAll
:
5054 case AT_DisableTrigUser
:
5055 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5056 /* Set up recursion for phase 2; no other prep needed */
5058 cmd
->recurse
= true;
5059 pass
= AT_PASS_MISC
;
5061 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
5062 case AT_EnableAlwaysRule
:
5063 case AT_EnableReplicaRule
:
5064 case AT_DisableRule
:
5065 case AT_AddOf
: /* OF */
5066 case AT_DropOf
: /* NOT OF */
5067 case AT_EnableRowSecurity
:
5068 case AT_DisableRowSecurity
:
5069 case AT_ForceRowSecurity
:
5070 case AT_NoForceRowSecurity
:
5071 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5072 /* These commands never recurse */
5073 /* No command-specific prep needed */
5074 pass
= AT_PASS_MISC
;
5076 case AT_GenericOptions
:
5077 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5078 /* No command-specific prep needed */
5079 pass
= AT_PASS_MISC
;
5081 case AT_AttachPartition
:
5082 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_INDEX
);
5083 /* No command-specific prep needed */
5084 pass
= AT_PASS_MISC
;
5086 case AT_DetachPartition
:
5087 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5088 /* No command-specific prep needed */
5089 pass
= AT_PASS_MISC
;
5091 case AT_DetachPartitionFinalize
:
5092 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5093 /* No command-specific prep needed */
5094 pass
= AT_PASS_MISC
;
5097 elog(ERROR
, "unrecognized alter table type: %d",
5098 (int) cmd
->subtype
);
5099 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
5102 Assert(pass
> AT_PASS_UNSET
);
5104 /* Add the subcommand to the appropriate list for phase 2 */
5105 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
5111 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5112 * dispatched in a "safe" execution order (designed to avoid unnecessary
5116 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
5117 AlterTableUtilityContext
*context
)
5123 * We process all the tables "in parallel", one pass at a time. This is
5124 * needed because we may have to propagate work from one table to another
5125 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5126 * re-adding of the foreign key constraint to the other table). Work can
5127 * only be propagated into later passes, however.
5129 for (pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
5131 /* Go through each table that needs to be processed */
5132 foreach(ltab
, *wqueue
)
5134 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5135 List
*subcmds
= tab
->subcmds
[pass
];
5142 * Open the relation and store it in tab. This allows subroutines
5143 * close and reopen, if necessary. Appropriate lock was obtained
5144 * by phase 1, needn't get it again.
5146 tab
->rel
= relation_open(tab
->relid
, NoLock
);
5148 foreach(lcmd
, subcmds
)
5149 ATExecCmd(wqueue
, tab
,
5150 lfirst_node(AlterTableCmd
, lcmd
),
5151 lockmode
, pass
, context
);
5154 * After the ALTER TYPE pass, do cleanup work (this is not done in
5155 * ATExecAlterColumnType since it should be done only once if
5156 * multiple columns of a table are altered).
5158 if (pass
== AT_PASS_ALTER_TYPE
)
5159 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
5163 relation_close(tab
->rel
, NoLock
);
5169 /* Check to see if a toast table must be added. */
5170 foreach(ltab
, *wqueue
)
5172 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5175 * If the table is source table of ATTACH PARTITION command, we did
5176 * not modify anything about it that will change its toasting
5177 * requirement, so no need to check.
5179 if (((tab
->relkind
== RELKIND_RELATION
||
5180 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5181 tab
->partition_constraint
== NULL
) ||
5182 tab
->relkind
== RELKIND_MATVIEW
)
5183 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5188 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5191 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5192 AlterTableCmd
*cmd
, LOCKMODE lockmode
, int cur_pass
,
5193 AlterTableUtilityContext
*context
)
5195 ObjectAddress address
= InvalidObjectAddress
;
5196 Relation rel
= tab
->rel
;
5198 switch (cmd
->subtype
)
5200 case AT_AddColumn
: /* ADD COLUMN */
5201 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5202 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5203 cmd
->recurse
, false,
5204 lockmode
, cur_pass
, context
);
5206 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5207 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5209 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5210 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5212 case AT_AddIdentity
:
5213 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5215 Assert(cmd
!= NULL
);
5216 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
);
5218 case AT_SetIdentity
:
5219 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5221 Assert(cmd
!= NULL
);
5222 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
);
5224 case AT_DropIdentity
:
5225 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5227 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5228 address
= ATExecDropNotNull(rel
, cmd
->name
, cmd
->recurse
, lockmode
);
5230 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5231 address
= ATExecSetNotNull(wqueue
, rel
, NULL
, cmd
->name
,
5232 cmd
->recurse
, false, NULL
, lockmode
);
5234 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull */
5235 address
= ATExecSetAttNotNull(wqueue
, rel
, cmd
->name
, lockmode
);
5237 case AT_DropExpression
:
5238 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5240 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5241 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5243 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5244 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5246 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5247 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5249 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5250 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5252 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5253 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5256 case AT_DropColumn
: /* DROP COLUMN */
5257 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5258 cmd
->behavior
, cmd
->recurse
, false,
5259 cmd
->missing_ok
, lockmode
,
5262 case AT_AddIndex
: /* ADD INDEX */
5263 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5266 case AT_ReAddIndex
: /* ADD INDEX */
5267 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5270 case AT_ReAddStatistics
: /* ADD STATISTICS */
5271 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5274 case AT_AddConstraint
: /* ADD CONSTRAINT */
5275 /* Transform the command only during initial examination */
5276 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5277 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5278 cmd
->recurse
, lockmode
,
5280 /* Depending on constraint type, might be no more work to do now */
5283 ATExecAddConstraint(wqueue
, tab
, rel
,
5284 (Constraint
*) cmd
->def
,
5285 cmd
->recurse
, false, lockmode
);
5287 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5289 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5290 true, true, lockmode
);
5292 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5295 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5296 ((AlterDomainStmt
*) cmd
->def
)->def
,
5299 case AT_ReAddComment
: /* Re-add existing comment */
5300 address
= CommentObject((CommentStmt
*) cmd
->def
);
5302 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5303 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5306 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5307 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5309 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5310 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5313 case AT_DropConstraint
: /* DROP CONSTRAINT */
5314 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5316 cmd
->missing_ok
, lockmode
);
5318 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5319 /* parse transformation was done earlier */
5320 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5322 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5324 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5325 (List
*) cmd
->def
, lockmode
);
5327 case AT_ChangeOwner
: /* ALTER OWNER */
5328 ATExecChangeOwner(RelationGetRelid(rel
),
5329 get_rolespec_oid(cmd
->newowner
, false),
5332 case AT_ClusterOn
: /* CLUSTER ON */
5333 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5335 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5336 ATExecDropCluster(rel
, lockmode
);
5338 case AT_SetLogged
: /* SET LOGGED */
5339 case AT_SetUnLogged
: /* SET UNLOGGED */
5341 case AT_DropOids
: /* SET WITHOUT OIDS */
5342 /* nothing to do here, oid columns don't exist anymore */
5344 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5345 /* handled specially in Phase 3 */
5347 case AT_SetTableSpace
: /* SET TABLESPACE */
5350 * Only do this for partitioned tables and indexes, for which this
5351 * is just a catalog change. Other relation types which have
5352 * storage are handled by Phase 3.
5354 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5355 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5356 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5359 case AT_SetRelOptions
: /* SET (...) */
5360 case AT_ResetRelOptions
: /* RESET (...) */
5361 case AT_ReplaceRelOptions
: /* replace entire option list */
5362 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5364 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5365 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5366 TRIGGER_FIRES_ON_ORIGIN
, false,
5370 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5371 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5372 TRIGGER_FIRES_ALWAYS
, false,
5376 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5377 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5378 TRIGGER_FIRES_ON_REPLICA
, false,
5382 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5383 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5384 TRIGGER_DISABLED
, false,
5388 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5389 ATExecEnableDisableTrigger(rel
, NULL
,
5390 TRIGGER_FIRES_ON_ORIGIN
, false,
5394 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5395 ATExecEnableDisableTrigger(rel
, NULL
,
5396 TRIGGER_DISABLED
, false,
5400 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5401 ATExecEnableDisableTrigger(rel
, NULL
,
5402 TRIGGER_FIRES_ON_ORIGIN
, true,
5406 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5407 ATExecEnableDisableTrigger(rel
, NULL
,
5408 TRIGGER_DISABLED
, true,
5413 case AT_EnableRule
: /* ENABLE RULE name */
5414 ATExecEnableDisableRule(rel
, cmd
->name
,
5415 RULE_FIRES_ON_ORIGIN
, lockmode
);
5417 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5418 ATExecEnableDisableRule(rel
, cmd
->name
,
5419 RULE_FIRES_ALWAYS
, lockmode
);
5421 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5422 ATExecEnableDisableRule(rel
, cmd
->name
,
5423 RULE_FIRES_ON_REPLICA
, lockmode
);
5425 case AT_DisableRule
: /* DISABLE RULE name */
5426 ATExecEnableDisableRule(rel
, cmd
->name
,
5427 RULE_DISABLED
, lockmode
);
5431 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5433 case AT_DropInherit
:
5434 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5437 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5440 ATExecDropOf(rel
, lockmode
);
5442 case AT_ReplicaIdentity
:
5443 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5445 case AT_EnableRowSecurity
:
5446 ATExecSetRowSecurity(rel
, true);
5448 case AT_DisableRowSecurity
:
5449 ATExecSetRowSecurity(rel
, false);
5451 case AT_ForceRowSecurity
:
5452 ATExecForceNoForceRowSecurity(rel
, true);
5454 case AT_NoForceRowSecurity
:
5455 ATExecForceNoForceRowSecurity(rel
, false);
5457 case AT_GenericOptions
:
5458 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5460 case AT_AttachPartition
:
5461 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5463 Assert(cmd
!= NULL
);
5464 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5465 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5468 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5469 ((PartitionCmd
*) cmd
->def
)->name
);
5471 case AT_DetachPartition
:
5472 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5474 Assert(cmd
!= NULL
);
5475 /* ATPrepCmd ensures it must be a table */
5476 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5477 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5478 ((PartitionCmd
*) cmd
->def
)->name
,
5479 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5481 case AT_DetachPartitionFinalize
:
5482 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5485 elog(ERROR
, "unrecognized alter table type: %d",
5486 (int) cmd
->subtype
);
5491 * Report the subcommand to interested event triggers.
5494 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5497 * Bump the command counter to ensure the next subcommand in the sequence
5498 * can see the changes so far
5500 CommandCounterIncrement();
5504 * ATParseTransformCmd: perform parse transformation for one subcommand
5506 * Returns the transformed subcommand tree, if there is one, else NULL.
5508 * The parser may hand back additional AlterTableCmd(s) and/or other
5509 * utility statements, either before or after the original subcommand.
5510 * Other AlterTableCmds are scheduled into the appropriate slot of the
5511 * AlteredTableInfo (they had better be for later passes than the current one).
5512 * Utility statements that are supposed to happen before the AlterTableCmd
5513 * are executed immediately. Those that are supposed to happen afterwards
5514 * are added to the tab->afterStmts list to be done at the very end.
5516 static AlterTableCmd
*
5517 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5518 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5519 int cur_pass
, AlterTableUtilityContext
*context
)
5521 AlterTableCmd
*newcmd
= NULL
;
5522 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5527 /* Gin up an AlterTableStmt with just this subcommand and this table */
5529 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5530 pstrdup(RelationGetRelationName(rel
)),
5532 atstmt
->relation
->inh
= recurse
;
5533 atstmt
->cmds
= list_make1(cmd
);
5534 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5535 atstmt
->missing_ok
= false;
5537 /* Transform the AlterTableStmt */
5538 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5540 context
->queryString
,
5544 /* Execute any statements that should happen before these subcommand(s) */
5545 foreach(lc
, beforeStmts
)
5547 Node
*stmt
= (Node
*) lfirst(lc
);
5549 ProcessUtilityForAlterTable(stmt
, context
);
5550 CommandCounterIncrement();
5553 /* Examine the transformed subcommands and schedule them appropriately */
5554 foreach(lc
, atstmt
->cmds
)
5556 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5560 * This switch need only cover the subcommand types that can be added
5561 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5562 * executing the subcommand immediately, as a substitute for the
5563 * original subcommand. (Note, however, that this does cause
5564 * AT_AddConstraint subcommands to be rescheduled into later passes,
5565 * which is important for index and foreign key constraints.)
5567 * We assume we needn't do any phase-1 checks for added subcommands.
5569 switch (cmd2
->subtype
)
5571 case AT_SetAttNotNull
:
5572 ATSimpleRecursion(wqueue
, rel
, cmd2
, recurse
, lockmode
, context
);
5573 pass
= AT_PASS_COL_ATTRS
;
5578 * A primary key on a inheritance parent needs supporting NOT
5579 * NULL constraint on its children; enqueue commands to create
5580 * those or mark them inherited if they already exist.
5582 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5583 pass
= AT_PASS_ADD_INDEX
;
5585 case AT_AddIndexConstraint
:
5587 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5588 pass
= AT_PASS_ADD_INDEXCONSTR
;
5590 case AT_AddConstraint
:
5591 /* Recursion occurs during execution phase */
5593 cmd2
->recurse
= true;
5594 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5596 case CONSTR_PRIMARY
:
5598 case CONSTR_EXCLUSION
:
5599 pass
= AT_PASS_ADD_INDEXCONSTR
;
5602 pass
= AT_PASS_ADD_OTHERCONSTR
;
5606 case AT_AlterColumnGenericOptions
:
5607 /* This command never recurses */
5608 /* No command-specific prep needed */
5609 pass
= AT_PASS_MISC
;
5616 if (pass
< cur_pass
)
5618 /* Cannot schedule into a pass we already finished */
5619 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5622 else if (pass
> cur_pass
)
5624 /* OK, queue it up for later */
5625 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5630 * We should see at most one subcommand for the current pass,
5631 * which is the transformed version of the original subcommand.
5633 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5635 /* Found the transformed version of our subcommand */
5639 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5644 /* Queue up any after-statements to happen at the end */
5645 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5651 * ATRewriteTables: ALTER TABLE phase 3
5654 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5655 AlterTableUtilityContext
*context
)
5659 /* Go through each table that needs to be checked or rewritten */
5660 foreach(ltab
, *wqueue
)
5662 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5664 /* Relations without storage may be ignored here */
5665 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5669 * If we change column data types, the operation has to be propagated
5670 * to tables that use this table's rowtype as a column type.
5671 * tab->newvals will also be non-NULL in the case where we're adding a
5672 * column with a default. We choose to forbid that case as well,
5673 * since composite types might eventually support defaults.
5675 * (Eventually we'll probably need to check for composite type
5676 * dependencies even when we're just scanning the table without a
5677 * rewrite, but at the moment a composite type does not enforce any
5678 * constraints, so it's not necessary/appropriate to enforce them just
5681 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5685 rel
= table_open(tab
->relid
, NoLock
);
5686 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5687 table_close(rel
, NoLock
);
5691 * We only need to rewrite the table if at least one column needs to
5692 * be recomputed, or we are changing its persistence or access method.
5694 * There are two reasons for requiring a rewrite when changing
5695 * persistence: on one hand, we need to ensure that the buffers
5696 * belonging to each of the two relations are marked with or without
5697 * BM_PERMANENT properly. On the other hand, since rewriting creates
5698 * and assigns a new relfilenumber, we automatically create or drop an
5699 * init fork for the relation as appropriate.
5701 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5703 /* Build a temporary relation and copy data */
5706 Oid NewAccessMethod
;
5710 OldHeap
= table_open(tab
->relid
, NoLock
);
5713 * We don't support rewriting of system catalogs; there are too
5714 * many corner cases and too little benefit. In particular this
5715 * is certainly not going to work for mapped catalogs.
5717 if (IsSystemRelation(OldHeap
))
5719 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5720 errmsg("cannot rewrite system relation \"%s\"",
5721 RelationGetRelationName(OldHeap
))));
5723 if (RelationIsUsedAsCatalogTable(OldHeap
))
5725 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5726 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5727 RelationGetRelationName(OldHeap
))));
5730 * Don't allow rewrite on temp tables of other backends ... their
5731 * local buffer manager is not going to cope.
5733 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5735 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5736 errmsg("cannot rewrite temporary tables of other sessions")));
5739 * Select destination tablespace (same as original unless user
5740 * requested a change)
5742 if (tab
->newTableSpace
)
5743 NewTableSpace
= tab
->newTableSpace
;
5745 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5748 * Select destination access method (same as original unless user
5749 * requested a change)
5751 if (OidIsValid(tab
->newAccessMethod
))
5752 NewAccessMethod
= tab
->newAccessMethod
;
5754 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5757 * Select persistence of transient table (same as original unless
5758 * user requested a change)
5760 persistence
= tab
->chgPersistence
?
5761 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5763 table_close(OldHeap
, NoLock
);
5766 * Fire off an Event Trigger now, before actually rewriting the
5769 * We don't support Event Trigger for nested commands anywhere,
5770 * here included, and parsetree is given NULL when coming from
5771 * AlterTableInternal.
5773 * And fire it only once.
5776 EventTriggerTableRewrite((Node
*) parsetree
,
5781 * Create transient table that will receive the modified data.
5783 * Ensure it is marked correctly as logged or unlogged. We have
5784 * to do this here so that buffers for the new relfilenumber will
5785 * have the right persistence set, and at the same time ensure
5786 * that the original filenumbers's buffers will get read in with
5787 * the correct setting (i.e. the original one). Otherwise a
5788 * rollback after the rewrite would possibly result with buffers
5789 * for the original filenumbers having the wrong persistence
5792 * NB: This relies on swap_relation_files() also swapping the
5793 * persistence. That wouldn't work for pg_class, but that can't be
5796 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5797 persistence
, lockmode
);
5800 * Copy the heap data into the new table with the desired
5801 * modifications, and test the current data within the table
5802 * against new constraints generated by ALTER TABLE commands.
5804 ATRewriteTable(tab
, OIDNewHeap
, lockmode
);
5807 * Swap the physical files of the old and new heaps, then rebuild
5808 * indexes and discard the old heap. We can use RecentXmin for
5809 * the table's new relfrozenxid because we rewrote all the tuples
5810 * in ATRewriteTable, so no older Xid remains in the table. Also,
5811 * we never try to swap toast tables by content, since we have no
5812 * interest in letting this code work on system catalogs.
5814 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5816 !OidIsValid(tab
->newTableSpace
),
5818 ReadNextMultiXactId(),
5821 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5823 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5825 if (tab
->chgPersistence
)
5826 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5831 * If required, test the current data within the table against new
5832 * constraints generated by ALTER TABLE commands, but don't
5835 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5836 tab
->partition_constraint
!= NULL
)
5837 ATRewriteTable(tab
, InvalidOid
, lockmode
);
5840 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5841 * just do a block-by-block copy.
5843 if (tab
->newTableSpace
)
5844 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5848 * Also change persistence of owned sequences, so that it matches the
5849 * table persistence.
5851 if (tab
->chgPersistence
)
5853 List
*seqlist
= getOwnedSequences(tab
->relid
);
5856 foreach(lc
, seqlist
)
5858 Oid seq_relid
= lfirst_oid(lc
);
5860 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5866 * Foreign key constraints are checked in a final pass, since (a) it's
5867 * generally best to examine each one separately, and (b) it's at least
5868 * theoretically possible that we have changed both relations of the
5869 * foreign key, and we'd better have finished both rewrites before we try
5870 * to read the tables.
5872 foreach(ltab
, *wqueue
)
5874 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5875 Relation rel
= NULL
;
5878 /* Relations without storage may be ignored here too */
5879 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5882 foreach(lcon
, tab
->constraints
)
5884 NewConstraint
*con
= lfirst(lcon
);
5886 if (con
->contype
== CONSTR_FOREIGN
)
5888 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
5893 /* Long since locked, no need for another */
5894 rel
= table_open(tab
->relid
, NoLock
);
5897 refrel
= table_open(con
->refrelid
, RowShareLock
);
5899 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
5904 * No need to mark the constraint row as validated, we did
5905 * that when we inserted the row earlier.
5908 table_close(refrel
, NoLock
);
5913 table_close(rel
, NoLock
);
5916 /* Finally, run any afterStmts that were queued up */
5917 foreach(ltab
, *wqueue
)
5919 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5922 foreach(lc
, tab
->afterStmts
)
5924 Node
*stmt
= (Node
*) lfirst(lc
);
5926 ProcessUtilityForAlterTable(stmt
, context
);
5927 CommandCounterIncrement();
5933 * ATRewriteTable: scan or rewrite one table
5935 * OIDNewHeap is InvalidOid if we don't need to rewrite
5938 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
)
5942 TupleDesc oldTupDesc
;
5943 TupleDesc newTupDesc
;
5944 bool needscan
= false;
5945 List
*notnull_attrs
;
5950 BulkInsertState bistate
;
5952 ExprState
*partqualstate
= NULL
;
5955 * Open the relation(s). We have surely already locked the existing
5958 oldrel
= table_open(tab
->relid
, NoLock
);
5959 oldTupDesc
= tab
->oldDesc
;
5960 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
5962 if (OidIsValid(OIDNewHeap
))
5963 newrel
= table_open(OIDNewHeap
, lockmode
);
5968 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5969 * is empty, so don't bother using it.
5973 mycid
= GetCurrentCommandId(true);
5974 bistate
= GetBulkInsertState();
5975 ti_options
= TABLE_INSERT_SKIP_FSM
;
5979 /* keep compiler quiet about using these uninitialized */
5986 * Generate the constraint and default execution states
5989 estate
= CreateExecutorState();
5991 /* Build the needed expression execution states */
5992 foreach(l
, tab
->constraints
)
5994 NewConstraint
*con
= lfirst(l
);
5996 switch (con
->contype
)
6000 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
6002 case CONSTR_FOREIGN
:
6003 /* Nothing to do here */
6006 elog(ERROR
, "unrecognized constraint type: %d",
6007 (int) con
->contype
);
6011 /* Build expression execution states for partition check quals */
6012 if (tab
->partition_constraint
)
6015 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
6018 foreach(l
, tab
->newvals
)
6020 NewColumnValue
*ex
= lfirst(l
);
6022 /* expr already planned */
6023 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
6026 notnull_attrs
= NIL
;
6027 if (newrel
|| tab
->verify_new_notnull
)
6030 * If we are rebuilding the tuples OR if we added any new but not
6031 * verified not-null constraints, check all not-null constraints. This
6032 * is a bit of overkill but it minimizes risk of bugs, and
6033 * heap_attisnull is a pretty cheap test anyway.
6035 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6037 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
6039 if (attr
->attnotnull
&& !attr
->attisdropped
)
6040 notnull_attrs
= lappend_int(notnull_attrs
, i
);
6046 if (newrel
|| needscan
)
6048 ExprContext
*econtext
;
6049 TupleTableSlot
*oldslot
;
6050 TupleTableSlot
*newslot
;
6052 MemoryContext oldCxt
;
6053 List
*dropped_attrs
= NIL
;
6059 (errmsg_internal("rewriting table \"%s\"",
6060 RelationGetRelationName(oldrel
))));
6063 (errmsg_internal("verifying table \"%s\"",
6064 RelationGetRelationName(oldrel
))));
6069 * All predicate locks on the tuples or pages are about to be made
6070 * invalid, because we move tuples around. Promote them to
6073 TransferPredicateLocksToHeapRelation(oldrel
);
6076 econtext
= GetPerTupleExprContext(estate
);
6079 * Create necessary tuple slots. When rewriting, two slots are needed,
6080 * otherwise one suffices. In the case where one slot suffices, we
6081 * need to use the new tuple descriptor, otherwise some constraints
6082 * can't be evaluated. Note that even when the tuple layout is the
6083 * same and no rewrite is required, the tupDescs might not be
6084 * (consider ADD COLUMN without a default).
6088 Assert(newrel
!= NULL
);
6089 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
6090 table_slot_callbacks(oldrel
));
6091 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
6092 table_slot_callbacks(newrel
));
6095 * Set all columns in the new slot to NULL initially, to ensure
6096 * columns added as part of the rewrite are initialized to NULL.
6097 * That is necessary as tab->newvals will not contain an
6098 * expression for columns with a NULL default, e.g. when adding a
6099 * column without a default together with a column with a default
6100 * requiring an actual rewrite.
6102 ExecStoreAllNullTuple(newslot
);
6106 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
6107 table_slot_callbacks(oldrel
));
6112 * Any attributes that are dropped according to the new tuple
6113 * descriptor can be set to NULL. We precompute the list of dropped
6114 * attributes to avoid needing to do so in the per-tuple loop.
6116 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6118 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
6119 dropped_attrs
= lappend_int(dropped_attrs
, i
);
6123 * Scan through the rows, generating a new row if needed and then
6124 * checking all the constraints.
6126 snapshot
= RegisterSnapshot(GetLatestSnapshot());
6127 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
6130 * Switch to per-tuple memory context and reset it for each tuple
6131 * produced, so we don't leak memory.
6133 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
6135 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
6137 TupleTableSlot
*insertslot
;
6139 if (tab
->rewrite
> 0)
6141 /* Extract data from old tuple */
6142 slot_getallattrs(oldslot
);
6143 ExecClearTuple(newslot
);
6145 /* copy attributes */
6146 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
6147 sizeof(Datum
) * oldslot
->tts_nvalid
);
6148 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
6149 sizeof(bool) * oldslot
->tts_nvalid
);
6151 /* Set dropped attributes to null in new tuple */
6152 foreach(lc
, dropped_attrs
)
6153 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
6156 * Constraints and GENERATED expressions might reference the
6157 * tableoid column, so fill tts_tableOid with the desired
6158 * value. (We must do this each time, because it gets
6159 * overwritten with newrel's OID during storing.)
6161 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
6164 * Process supplied expressions to replace selected columns.
6166 * First, evaluate expressions whose inputs come from the old
6169 econtext
->ecxt_scantuple
= oldslot
;
6171 foreach(l
, tab
->newvals
)
6173 NewColumnValue
*ex
= lfirst(l
);
6175 if (ex
->is_generated
)
6178 newslot
->tts_values
[ex
->attnum
- 1]
6179 = ExecEvalExpr(ex
->exprstate
,
6181 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6184 ExecStoreVirtualTuple(newslot
);
6187 * Now, evaluate any expressions whose inputs come from the
6188 * new tuple. We assume these columns won't reference each
6189 * other, so that there's no ordering dependency.
6191 econtext
->ecxt_scantuple
= newslot
;
6193 foreach(l
, tab
->newvals
)
6195 NewColumnValue
*ex
= lfirst(l
);
6197 if (!ex
->is_generated
)
6200 newslot
->tts_values
[ex
->attnum
- 1]
6201 = ExecEvalExpr(ex
->exprstate
,
6203 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6206 insertslot
= newslot
;
6211 * If there's no rewrite, old and new table are guaranteed to
6212 * have the same AM, so we can just use the old slot to verify
6213 * new constraints etc.
6215 insertslot
= oldslot
;
6218 /* Now check any constraints on the possibly-changed tuple */
6219 econtext
->ecxt_scantuple
= insertslot
;
6221 foreach(l
, notnull_attrs
)
6223 int attn
= lfirst_int(l
);
6225 if (slot_attisnull(insertslot
, attn
+ 1))
6227 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6230 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6231 errmsg("column \"%s\" of relation \"%s\" contains null values",
6232 NameStr(attr
->attname
),
6233 RelationGetRelationName(oldrel
)),
6234 errtablecol(oldrel
, attn
+ 1)));
6238 foreach(l
, tab
->constraints
)
6240 NewConstraint
*con
= lfirst(l
);
6242 switch (con
->contype
)
6245 if (!ExecCheck(con
->qualstate
, econtext
))
6247 (errcode(ERRCODE_CHECK_VIOLATION
),
6248 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6250 RelationGetRelationName(oldrel
)),
6251 errtableconstraint(oldrel
, con
->name
)));
6253 case CONSTR_NOTNULL
:
6254 case CONSTR_FOREIGN
:
6255 /* Nothing to do here */
6258 elog(ERROR
, "unrecognized constraint type: %d",
6259 (int) con
->contype
);
6263 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6265 if (tab
->validate_default
)
6267 (errcode(ERRCODE_CHECK_VIOLATION
),
6268 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6269 RelationGetRelationName(oldrel
)),
6273 (errcode(ERRCODE_CHECK_VIOLATION
),
6274 errmsg("partition constraint of relation \"%s\" is violated by some row",
6275 RelationGetRelationName(oldrel
)),
6279 /* Write the tuple out to the new relation */
6281 table_tuple_insert(newrel
, insertslot
, mycid
,
6282 ti_options
, bistate
);
6284 ResetExprContext(econtext
);
6286 CHECK_FOR_INTERRUPTS();
6289 MemoryContextSwitchTo(oldCxt
);
6290 table_endscan(scan
);
6291 UnregisterSnapshot(snapshot
);
6293 ExecDropSingleTupleTableSlot(oldslot
);
6295 ExecDropSingleTupleTableSlot(newslot
);
6298 FreeExecutorState(estate
);
6300 table_close(oldrel
, NoLock
);
6303 FreeBulkInsertState(bistate
);
6305 table_finish_bulk_insert(newrel
, ti_options
);
6307 table_close(newrel
, NoLock
);
6312 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6314 static AlteredTableInfo
*
6315 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6317 Oid relid
= RelationGetRelid(rel
);
6318 AlteredTableInfo
*tab
;
6321 foreach(ltab
, *wqueue
)
6323 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6324 if (tab
->relid
== relid
)
6329 * Not there, so add it. Note that we make a copy of the relation's
6330 * existing descriptor before anything interesting can happen to it.
6332 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6334 tab
->rel
= NULL
; /* set later */
6335 tab
->relkind
= rel
->rd_rel
->relkind
;
6336 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6337 tab
->newAccessMethod
= InvalidOid
;
6338 tab
->newTableSpace
= InvalidOid
;
6339 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6340 tab
->chgPersistence
= false;
6342 *wqueue
= lappend(*wqueue
, tab
);
6348 alter_table_type_to_string(AlterTableType cmdtype
)
6353 case AT_AddColumnToView
:
6354 return "ADD COLUMN";
6355 case AT_ColumnDefault
:
6356 case AT_CookedColumnDefault
:
6357 return "ALTER COLUMN ... SET DEFAULT";
6358 case AT_DropNotNull
:
6359 return "ALTER COLUMN ... DROP NOT NULL";
6361 return "ALTER COLUMN ... SET NOT NULL";
6362 case AT_SetAttNotNull
:
6363 return NULL
; /* not real grammar */
6364 case AT_DropExpression
:
6365 return "ALTER COLUMN ... DROP EXPRESSION";
6366 case AT_SetStatistics
:
6367 return "ALTER COLUMN ... SET STATISTICS";
6369 return "ALTER COLUMN ... SET";
6370 case AT_ResetOptions
:
6371 return "ALTER COLUMN ... RESET";
6373 return "ALTER COLUMN ... SET STORAGE";
6374 case AT_SetCompression
:
6375 return "ALTER COLUMN ... SET COMPRESSION";
6377 return "DROP COLUMN";
6380 return NULL
; /* not real grammar */
6381 case AT_AddConstraint
:
6382 case AT_ReAddConstraint
:
6383 case AT_ReAddDomainConstraint
:
6384 case AT_AddIndexConstraint
:
6385 return "ADD CONSTRAINT";
6386 case AT_AlterConstraint
:
6387 return "ALTER CONSTRAINT";
6388 case AT_ValidateConstraint
:
6389 return "VALIDATE CONSTRAINT";
6390 case AT_DropConstraint
:
6391 return "DROP CONSTRAINT";
6392 case AT_ReAddComment
:
6393 return NULL
; /* not real grammar */
6394 case AT_AlterColumnType
:
6395 return "ALTER COLUMN ... SET DATA TYPE";
6396 case AT_AlterColumnGenericOptions
:
6397 return "ALTER COLUMN ... OPTIONS";
6398 case AT_ChangeOwner
:
6401 return "CLUSTER ON";
6402 case AT_DropCluster
:
6403 return "SET WITHOUT CLUSTER";
6404 case AT_SetAccessMethod
:
6405 return "SET ACCESS METHOD";
6407 return "SET LOGGED";
6408 case AT_SetUnLogged
:
6409 return "SET UNLOGGED";
6411 return "SET WITHOUT OIDS";
6412 case AT_SetTableSpace
:
6413 return "SET TABLESPACE";
6414 case AT_SetRelOptions
:
6416 case AT_ResetRelOptions
:
6418 case AT_ReplaceRelOptions
:
6419 return NULL
; /* not real grammar */
6421 return "ENABLE TRIGGER";
6422 case AT_EnableAlwaysTrig
:
6423 return "ENABLE ALWAYS TRIGGER";
6424 case AT_EnableReplicaTrig
:
6425 return "ENABLE REPLICA TRIGGER";
6426 case AT_DisableTrig
:
6427 return "DISABLE TRIGGER";
6428 case AT_EnableTrigAll
:
6429 return "ENABLE TRIGGER ALL";
6430 case AT_DisableTrigAll
:
6431 return "DISABLE TRIGGER ALL";
6432 case AT_EnableTrigUser
:
6433 return "ENABLE TRIGGER USER";
6434 case AT_DisableTrigUser
:
6435 return "DISABLE TRIGGER USER";
6437 return "ENABLE RULE";
6438 case AT_EnableAlwaysRule
:
6439 return "ENABLE ALWAYS RULE";
6440 case AT_EnableReplicaRule
:
6441 return "ENABLE REPLICA RULE";
6442 case AT_DisableRule
:
6443 return "DISABLE RULE";
6446 case AT_DropInherit
:
6447 return "NO INHERIT";
6452 case AT_ReplicaIdentity
:
6453 return "REPLICA IDENTITY";
6454 case AT_EnableRowSecurity
:
6455 return "ENABLE ROW SECURITY";
6456 case AT_DisableRowSecurity
:
6457 return "DISABLE ROW SECURITY";
6458 case AT_ForceRowSecurity
:
6459 return "FORCE ROW SECURITY";
6460 case AT_NoForceRowSecurity
:
6461 return "NO FORCE ROW SECURITY";
6462 case AT_GenericOptions
:
6464 case AT_AttachPartition
:
6465 return "ATTACH PARTITION";
6466 case AT_DetachPartition
:
6467 return "DETACH PARTITION";
6468 case AT_DetachPartitionFinalize
:
6469 return "DETACH PARTITION ... FINALIZE";
6470 case AT_AddIdentity
:
6471 return "ALTER COLUMN ... ADD IDENTITY";
6472 case AT_SetIdentity
:
6473 return "ALTER COLUMN ... SET";
6474 case AT_DropIdentity
:
6475 return "ALTER COLUMN ... DROP IDENTITY";
6476 case AT_ReAddStatistics
:
6477 return NULL
; /* not real grammar */
6484 * ATSimplePermissions
6486 * - Ensure that it is a relation (or possibly a view)
6487 * - Ensure this user is the owner
6488 * - Ensure that it is not a system table
6491 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6495 switch (rel
->rd_rel
->relkind
)
6497 case RELKIND_RELATION
:
6498 case RELKIND_PARTITIONED_TABLE
:
6499 actual_target
= ATT_TABLE
;
6502 actual_target
= ATT_VIEW
;
6504 case RELKIND_MATVIEW
:
6505 actual_target
= ATT_MATVIEW
;
6508 actual_target
= ATT_INDEX
;
6510 case RELKIND_PARTITIONED_INDEX
:
6511 actual_target
= ATT_PARTITIONED_INDEX
;
6513 case RELKIND_COMPOSITE_TYPE
:
6514 actual_target
= ATT_COMPOSITE_TYPE
;
6516 case RELKIND_FOREIGN_TABLE
:
6517 actual_target
= ATT_FOREIGN_TABLE
;
6519 case RELKIND_SEQUENCE
:
6520 actual_target
= ATT_SEQUENCE
;
6527 /* Wrong target type? */
6528 if ((actual_target
& allowed_targets
) == 0)
6530 const char *action_str
= alter_table_type_to_string(cmdtype
);
6534 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6535 /* translator: %s is a group of some SQL keywords */
6536 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6537 action_str
, RelationGetRelationName(rel
)),
6538 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6540 /* internal error? */
6541 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6542 RelationGetRelationName(rel
));
6545 /* Permissions checks */
6546 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6547 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6548 RelationGetRelationName(rel
));
6550 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6552 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6553 errmsg("permission denied: \"%s\" is a system catalog",
6554 RelationGetRelationName(rel
))));
6560 * Simple table recursion sufficient for most ALTER TABLE operations.
6561 * All direct and indirect children are processed in an unspecified order.
6562 * Note that if a child inherits from the original table via multiple
6563 * inheritance paths, it will be visited just once.
6566 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6567 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6568 AlterTableUtilityContext
*context
)
6571 * Propagate to children, if desired and if there are (or might be) any
6574 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6576 Oid relid
= RelationGetRelid(rel
);
6580 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6583 * find_all_inheritors does the recursive search of the inheritance
6584 * hierarchy, so all we have to do is process all of the relids in the
6585 * list that it returns.
6587 foreach(child
, children
)
6589 Oid childrelid
= lfirst_oid(child
);
6592 if (childrelid
== relid
)
6594 /* find_all_inheritors already got lock */
6595 childrel
= relation_open(childrelid
, NoLock
);
6596 CheckTableNotInUse(childrel
, "ALTER TABLE");
6597 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6598 relation_close(childrel
, NoLock
);
6604 * Obtain list of partitions of the given table, locking them all at the given
6605 * lockmode and ensuring that they all pass CheckTableNotInUse.
6607 * This function is a no-op if the given relation is not a partitioned table;
6608 * in particular, nothing is done if it's a legacy inheritance parent.
6611 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6613 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6618 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6619 /* first element is the parent rel; must ignore it */
6620 for_each_from(cell
, inh
, 1)
6624 /* find_all_inheritors already got lock */
6625 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6626 CheckTableNotInUse(childrel
, "ALTER TABLE");
6627 table_close(childrel
, NoLock
);
6634 * ATTypedTableRecursion
6636 * Propagate ALTER TYPE operations to the typed tables of that type.
6637 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6638 * recursion to inheritance children of the typed tables.
6641 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6642 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6647 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6649 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6650 RelationGetRelationName(rel
),
6653 foreach(child
, children
)
6655 Oid childrelid
= lfirst_oid(child
);
6658 childrel
= relation_open(childrelid
, lockmode
);
6659 CheckTableNotInUse(childrel
, "ALTER TABLE");
6660 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6661 relation_close(childrel
, NoLock
);
6667 * find_composite_type_dependencies
6669 * Check to see if the type "typeOid" is being used as a column in some table
6670 * (possibly nested several levels deep in composite types, arrays, etc!).
6671 * Eventually, we'd like to propagate the check or rewrite operation
6672 * into such tables, but for now, just error out if we find any.
6674 * Caller should provide either the associated relation of a rowtype,
6675 * or a type name (not both) for use in the error message, if any.
6677 * Note that "typeOid" is not necessarily a composite type; it could also be
6678 * another container type such as an array or range, or a domain over one of
6679 * these things. The name of this function is therefore somewhat historical,
6680 * but it's not worth changing.
6682 * We assume that functions and views depending on the type are not reasons
6683 * to reject the ALTER. (How safe is this really?)
6686 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6687 const char *origTypeName
)
6691 SysScanDesc depScan
;
6694 /* since this function recurses, it could be driven to stack overflow */
6695 check_stack_depth();
6698 * We scan pg_depend to find those things that depend on the given type.
6699 * (We assume we can ignore refobjsubid for a type.)
6701 depRel
= table_open(DependRelationId
, AccessShareLock
);
6703 ScanKeyInit(&key
[0],
6704 Anum_pg_depend_refclassid
,
6705 BTEqualStrategyNumber
, F_OIDEQ
,
6706 ObjectIdGetDatum(TypeRelationId
));
6707 ScanKeyInit(&key
[1],
6708 Anum_pg_depend_refobjid
,
6709 BTEqualStrategyNumber
, F_OIDEQ
,
6710 ObjectIdGetDatum(typeOid
));
6712 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6715 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6717 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6719 TupleDesc tupleDesc
;
6720 Form_pg_attribute att
;
6722 /* Check for directly dependent types */
6723 if (pg_depend
->classid
== TypeRelationId
)
6726 * This must be an array, domain, or range containing the given
6727 * type, so recursively check for uses of this type. Note that
6728 * any error message will mention the original type not the
6729 * container; this is intentional.
6731 find_composite_type_dependencies(pg_depend
->objid
,
6732 origRelation
, origTypeName
);
6736 /* Else, ignore dependees that aren't relations */
6737 if (pg_depend
->classid
!= RelationRelationId
)
6740 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6741 tupleDesc
= RelationGetDescr(rel
);
6744 * If objsubid identifies a specific column, refer to that in error
6745 * messages. Otherwise, search to see if there's a user column of the
6746 * type. (We assume system columns are never of interesting types.)
6747 * The search is needed because an index containing an expression
6748 * column of the target type will just be recorded as a whole-relation
6749 * dependency. If we do not find a column of the type, the dependency
6750 * must indicate that the type is transiently referenced in an index
6751 * expression but not stored on disk, which we assume is OK, just as
6752 * we do for references in views. (It could also be that the target
6753 * type is embedded in some container type that is stored in an index
6754 * column, but the previous recursion should catch such cases.)
6756 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6757 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6761 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6763 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6764 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6770 /* No such column, so assume OK */
6771 relation_close(rel
, AccessShareLock
);
6777 * We definitely should reject if the relation has storage. If it's
6778 * partitioned, then perhaps we don't have to reject: if there are
6779 * partitions then we'll fail when we find one, else there is no
6780 * stored data to worry about. However, it's possible that the type
6781 * change would affect conclusions about whether the type is sortable
6782 * or hashable and thus (if it's a partitioning column) break the
6783 * partitioning rule. For now, reject for partitioned rels too.
6785 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6786 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6790 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6791 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6793 RelationGetRelationName(rel
),
6794 NameStr(att
->attname
))));
6795 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6797 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6798 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6799 RelationGetRelationName(origRelation
),
6800 RelationGetRelationName(rel
),
6801 NameStr(att
->attname
))));
6802 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6804 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6805 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6806 RelationGetRelationName(origRelation
),
6807 RelationGetRelationName(rel
),
6808 NameStr(att
->attname
))));
6811 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6812 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6813 RelationGetRelationName(origRelation
),
6814 RelationGetRelationName(rel
),
6815 NameStr(att
->attname
))));
6817 else if (OidIsValid(rel
->rd_rel
->reltype
))
6820 * A view or composite type itself isn't a problem, but we must
6821 * recursively check for indirect dependencies via its rowtype.
6823 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6824 origRelation
, origTypeName
);
6827 relation_close(rel
, AccessShareLock
);
6830 systable_endscan(depScan
);
6832 relation_close(depRel
, AccessShareLock
);
6837 * find_typed_table_dependencies
6839 * Check to see if a composite type is being used as the type of a
6840 * typed table. Abort if any are found and behavior is RESTRICT.
6841 * Else return the list of tables.
6844 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6852 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6854 ScanKeyInit(&key
[0],
6855 Anum_pg_class_reloftype
,
6856 BTEqualStrategyNumber
, F_OIDEQ
,
6857 ObjectIdGetDatum(typeOid
));
6859 scan
= table_beginscan_catalog(classRel
, 1, key
);
6861 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6863 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6865 if (behavior
== DROP_RESTRICT
)
6867 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6868 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6870 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6872 result
= lappend_oid(result
, classform
->oid
);
6875 table_endscan(scan
);
6876 table_close(classRel
, AccessShareLock
);
6885 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6886 * isn't suitable, throw an error. Currently, we require that the type
6887 * originated with CREATE TYPE AS. We could support any row type, but doing so
6888 * would require handling a number of extra corner cases in the DDL commands.
6889 * (Also, allowing domain-over-composite would open up a can of worms about
6890 * whether and how the domain's constraints should apply to derived tables.)
6893 check_of_type(HeapTuple typetuple
)
6895 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
6896 bool typeOk
= false;
6898 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
6900 Relation typeRelation
;
6902 Assert(OidIsValid(typ
->typrelid
));
6903 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
6904 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6907 * Close the parent rel, but keep our AccessShareLock on it until xact
6908 * commit. That will prevent someone else from deleting or ALTERing
6909 * the type before the typed table creation/conversion commits.
6911 relation_close(typeRelation
, NoLock
);
6915 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6916 errmsg("type %s is not a composite type",
6917 format_type_be(typ
->oid
))));
6922 * ALTER TABLE ADD COLUMN
6924 * Adds an additional attribute to a relation making the assumption that
6925 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6926 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6929 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6930 * have to decide at runtime whether to recurse or not depending on whether we
6931 * actually add a column or merely merge with an existing column. (We can't
6932 * check this in a static pre-pass because it won't handle multiple inheritance
6933 * situations correctly.)
6936 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
6937 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
6938 AlterTableUtilityContext
*context
)
6940 if (rel
->rd_rel
->reloftype
&& !recursing
)
6942 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6943 errmsg("cannot add column to typed table")));
6945 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6946 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
6948 if (recurse
&& !is_view
)
6949 cmd
->recurse
= true;
6953 * Add a column to a table. The return value is the address of the
6954 * new column in the parent relation.
6956 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6957 * copy (but that happens only after we check for IF NOT EXISTS).
6959 static ObjectAddress
6960 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
6961 AlterTableCmd
**cmd
, bool recurse
, bool recursing
,
6962 LOCKMODE lockmode
, int cur_pass
,
6963 AlterTableUtilityContext
*context
)
6965 Oid myrelid
= RelationGetRelid(rel
);
6966 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
6967 bool if_not_exists
= (*cmd
)->missing_ok
;
6971 FormData_pg_attribute attribute
;
6974 HeapTuple typeTuple
;
6982 AlterTableCmd
*childcmd
;
6983 AclResult aclresult
;
6984 ObjectAddress address
;
6986 FormData_pg_attribute
*aattr
[] = {&attribute
};
6988 /* At top level, permission check was done in ATPrepCmd, else do it */
6990 ATSimplePermissions((*cmd
)->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
6992 if (rel
->rd_rel
->relispartition
&& !recursing
)
6994 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6995 errmsg("cannot add column to a partition")));
6997 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
7000 * Are we adding the column to a recursion child? If so, check whether to
7001 * merge with an existing definition for the column. If we do merge, we
7002 * must not recurse. Children will already have the column, and recursing
7003 * into them would mess up attinhcount.
7005 if (colDef
->inhcount
> 0)
7009 /* Does child already have a column by this name? */
7010 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
7011 if (HeapTupleIsValid(tuple
))
7013 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7018 /* Child column must match on type, typmod, and collation */
7019 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
7020 if (ctypeId
!= childatt
->atttypid
||
7021 ctypmod
!= childatt
->atttypmod
)
7023 (errcode(ERRCODE_DATATYPE_MISMATCH
),
7024 errmsg("child table \"%s\" has different type for column \"%s\"",
7025 RelationGetRelationName(rel
), colDef
->colname
)));
7026 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
7027 if (ccollid
!= childatt
->attcollation
)
7029 (errcode(ERRCODE_COLLATION_MISMATCH
),
7030 errmsg("child table \"%s\" has different collation for column \"%s\"",
7031 RelationGetRelationName(rel
), colDef
->colname
),
7032 errdetail("\"%s\" versus \"%s\"",
7033 get_collation_name(ccollid
),
7034 get_collation_name(childatt
->attcollation
))));
7036 /* Bump the existing child att's inhcount */
7037 childatt
->attinhcount
++;
7038 if (childatt
->attinhcount
< 0)
7040 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7041 errmsg("too many inheritance parents"));
7042 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
7044 heap_freetuple(tuple
);
7046 /* Inform the user about the merge */
7048 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7049 colDef
->colname
, RelationGetRelationName(rel
))));
7051 table_close(attrdesc
, RowExclusiveLock
);
7052 return InvalidObjectAddress
;
7056 /* skip if the name already exists and if_not_exists is true */
7057 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
7059 table_close(attrdesc
, RowExclusiveLock
);
7060 return InvalidObjectAddress
;
7064 * Okay, we need to add the column, so go ahead and do parse
7065 * transformation. This can result in queueing up, or even immediately
7066 * executing, subsidiary operations (such as creation of unique indexes);
7067 * so we mustn't do it until we have made the if_not_exists check.
7069 * When recursing, the command was already transformed and we needn't do
7070 * so again. Also, if context isn't given we can't transform. (That
7071 * currently happens only for AT_AddColumnToView; we expect that view.c
7072 * passed us a ColumnDef that doesn't need work.)
7074 if (context
!= NULL
&& !recursing
)
7076 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
7078 Assert(*cmd
!= NULL
);
7079 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7083 * Cannot add identity column if table has children, because identity does
7084 * not inherit. (Adding column and identity separately will work.)
7086 if (colDef
->identity
&&
7088 find_inheritance_children(myrelid
, NoLock
) != NIL
)
7090 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7091 errmsg("cannot recursively add identity column to table that has child tables")));
7093 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
7095 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
7096 if (!HeapTupleIsValid(reltup
))
7097 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
7098 relkind
= ((Form_pg_class
) GETSTRUCT(reltup
))->relkind
;
7100 /* Determine the new attribute's number */
7101 newattnum
= ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
+ 1;
7102 if (newattnum
> MaxHeapAttributeNumber
)
7104 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
7105 errmsg("tables can have at most %d columns",
7106 MaxHeapAttributeNumber
)));
7108 typeTuple
= typenameType(NULL
, colDef
->typeName
, &typmod
);
7109 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
7110 typeOid
= tform
->oid
;
7112 aclresult
= object_aclcheck(TypeRelationId
, typeOid
, GetUserId(), ACL_USAGE
);
7113 if (aclresult
!= ACLCHECK_OK
)
7114 aclcheck_error_type(aclresult
, typeOid
);
7116 collOid
= GetColumnDefCollation(NULL
, colDef
, typeOid
);
7118 /* make sure datatype is legal for a column */
7119 CheckAttributeType(colDef
->colname
, typeOid
, collOid
,
7120 list_make1_oid(rel
->rd_rel
->reltype
),
7124 * Construct new attribute's pg_attribute entry. (Variable-length fields
7125 * are handled by InsertPgAttributeTuples().)
7127 attribute
.attrelid
= myrelid
;
7128 namestrcpy(&(attribute
.attname
), colDef
->colname
);
7129 attribute
.atttypid
= typeOid
;
7130 attribute
.attstattarget
= -1;
7131 attribute
.attlen
= tform
->typlen
;
7132 attribute
.attnum
= newattnum
;
7133 if (list_length(colDef
->typeName
->arrayBounds
) > PG_INT16_MAX
)
7135 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7136 errmsg("too many array dimensions"));
7137 attribute
.attndims
= list_length(colDef
->typeName
->arrayBounds
);
7138 attribute
.atttypmod
= typmod
;
7139 attribute
.attbyval
= tform
->typbyval
;
7140 attribute
.attalign
= tform
->typalign
;
7141 if (colDef
->storage_name
)
7142 attribute
.attstorage
= GetAttributeStorage(typeOid
, colDef
->storage_name
);
7144 attribute
.attstorage
= tform
->typstorage
;
7145 attribute
.attcompression
= GetAttributeCompression(typeOid
,
7146 colDef
->compression
);
7147 attribute
.attnotnull
= colDef
->is_not_null
;
7148 attribute
.atthasdef
= false;
7149 attribute
.atthasmissing
= false;
7150 attribute
.attidentity
= colDef
->identity
;
7151 attribute
.attgenerated
= colDef
->generated
;
7152 attribute
.attisdropped
= false;
7153 attribute
.attislocal
= colDef
->is_local
;
7154 attribute
.attinhcount
= colDef
->inhcount
;
7155 attribute
.attcollation
= collOid
;
7157 ReleaseSysCache(typeTuple
);
7159 tupdesc
= CreateTupleDesc(lengthof(aattr
), (FormData_pg_attribute
**) &aattr
);
7161 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
7163 table_close(attrdesc
, RowExclusiveLock
);
7166 * Update pg_class tuple as appropriate
7168 ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
= newattnum
;
7170 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
7172 heap_freetuple(reltup
);
7174 /* Post creation hook for new attribute */
7175 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
7177 table_close(pgclass
, RowExclusiveLock
);
7179 /* Make the attribute's catalog entry visible */
7180 CommandCounterIncrement();
7183 * Store the DEFAULT, if any, in the catalogs
7185 if (colDef
->raw_default
)
7187 RawColumnDefault
*rawEnt
;
7189 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7190 rawEnt
->attnum
= attribute
.attnum
;
7191 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7194 * Attempt to skip a complete table rewrite by storing the specified
7195 * DEFAULT value outside of the heap. This may be disabled inside
7196 * AddRelationNewConstraints if the optimization cannot be applied.
7198 rawEnt
->missingMode
= (!colDef
->generated
);
7200 rawEnt
->generated
= colDef
->generated
;
7203 * This function is intended for CREATE TABLE, so it processes a
7204 * _list_ of defaults, but we just do one.
7206 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7207 false, true, false, NULL
);
7209 /* Make the additional catalog changes visible */
7210 CommandCounterIncrement();
7213 * Did the request for a missing value work? If not we'll have to do a
7216 if (!rawEnt
->missingMode
)
7217 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7221 * Tell Phase 3 to fill in the default expression, if there is one.
7223 * If there is no default, Phase 3 doesn't have to do anything, because
7224 * that effectively means that the default is NULL. The heap tuple access
7225 * routines always check for attnum > # of attributes in tuple, and return
7226 * NULL if so, so without any modification of the tuple data we will get
7227 * the effect of NULL values in the new column.
7229 * An exception occurs when the new column is of a domain type: the domain
7230 * might have a not-null constraint, or a check constraint that indirectly
7231 * rejects nulls. If there are any domain constraints then we construct
7232 * an explicit NULL default value that will be passed through
7233 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7234 * rewriting the table which we really don't have to do, but the present
7235 * design of domain processing doesn't offer any simple way of checking
7236 * the constraints more directly.)
7238 * Note: we use build_column_default, and not just the cooked default
7239 * returned by AddRelationNewConstraints, so that the right thing happens
7240 * when a datatype's default applies.
7242 * Note: it might seem that this should happen at the end of Phase 2, so
7243 * that the effects of subsequent subcommands can be taken into account.
7244 * It's intentional that we do it now, though. The new column should be
7245 * filled according to what is said in the ADD COLUMN subcommand, so that
7246 * the effects are the same as if this subcommand had been run by itself
7247 * and the later subcommands had been issued in new ALTER TABLE commands.
7249 * We can skip this entirely for relations without storage, since Phase 3
7250 * is certainly not going to touch them. System attributes don't have
7251 * interesting defaults, either.
7253 if (RELKIND_HAS_STORAGE(relkind
))
7256 * For an identity column, we can't use build_column_default(),
7257 * because the sequence ownership isn't set yet. So do it manually.
7259 if (colDef
->identity
)
7261 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7263 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7264 nve
->typeId
= typeOid
;
7266 defval
= (Expr
*) nve
;
7268 /* must do a rewrite for identity columns */
7269 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7272 defval
= (Expr
*) build_column_default(rel
, attribute
.attnum
);
7274 if (!defval
&& DomainHasConstraints(typeOid
))
7280 baseTypeMod
= typmod
;
7281 baseTypeId
= getBaseTypeAndTypmod(typeOid
, &baseTypeMod
);
7282 baseTypeColl
= get_typcollation(baseTypeId
);
7283 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7284 defval
= (Expr
*) coerce_to_target_type(NULL
,
7289 COERCION_ASSIGNMENT
,
7290 COERCE_IMPLICIT_CAST
,
7292 if (defval
== NULL
) /* should not happen */
7293 elog(ERROR
, "failed to coerce base type to domain");
7298 NewColumnValue
*newval
;
7300 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7301 newval
->attnum
= attribute
.attnum
;
7302 newval
->expr
= expression_planner(defval
);
7303 newval
->is_generated
= (colDef
->generated
!= '\0');
7305 tab
->newvals
= lappend(tab
->newvals
, newval
);
7308 if (DomainHasConstraints(typeOid
))
7309 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7311 if (!TupleDescAttr(rel
->rd_att
, attribute
.attnum
- 1)->atthasmissing
)
7314 * If the new column is NOT NULL, and there is no missing value,
7315 * tell Phase 3 it needs to check for NULLs.
7317 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7322 * Add needed dependency entries for the new column.
7324 add_column_datatype_dependency(myrelid
, newattnum
, attribute
.atttypid
);
7325 add_column_collation_dependency(myrelid
, newattnum
, attribute
.attcollation
);
7328 * Propagate to children as appropriate. Unlike most other ALTER
7329 * routines, we have to do this one level of recursion at a time; we can't
7330 * use find_all_inheritors to do it in one pass.
7333 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7336 * If we are told not to recurse, there had better not be any child
7337 * tables; else the addition would put them out of step.
7339 if (children
&& !recurse
)
7341 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7342 errmsg("column must be added to child tables too")));
7344 /* Children should see column as singly inherited */
7347 childcmd
= copyObject(*cmd
);
7348 colDef
= castNode(ColumnDef
, childcmd
->def
);
7349 colDef
->inhcount
= 1;
7350 colDef
->is_local
= false;
7353 childcmd
= *cmd
; /* no need to copy again */
7355 foreach(child
, children
)
7357 Oid childrelid
= lfirst_oid(child
);
7359 AlteredTableInfo
*childtab
;
7361 /* find_inheritance_children already got lock */
7362 childrel
= table_open(childrelid
, NoLock
);
7363 CheckTableNotInUse(childrel
, "ALTER TABLE");
7365 /* Find or create work queue entry for this table */
7366 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7368 /* Recurse to child; return value is ignored */
7369 ATExecAddColumn(wqueue
, childtab
, childrel
,
7370 &childcmd
, recurse
, true,
7371 lockmode
, cur_pass
, context
);
7373 table_close(childrel
, NoLock
);
7376 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7381 * If a new or renamed column will collide with the name of an existing
7382 * column and if_not_exists is false then error out, else do nothing.
7385 check_for_column_name_collision(Relation rel
, const char *colname
,
7392 * this test is deliberately not attisdropped-aware, since if one tries to
7393 * add a column matching a dropped column name, it's gonna fail anyway.
7395 attTuple
= SearchSysCache2(ATTNAME
,
7396 ObjectIdGetDatum(RelationGetRelid(rel
)),
7397 PointerGetDatum(colname
));
7398 if (!HeapTupleIsValid(attTuple
))
7401 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7402 ReleaseSysCache(attTuple
);
7405 * We throw a different error message for conflicts with system column
7406 * names, since they are normally not shown and the user might otherwise
7407 * be confused about the reason for the conflict.
7411 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7412 errmsg("column name \"%s\" conflicts with a system column name",
7419 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7420 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7421 colname
, RelationGetRelationName(rel
))));
7426 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7427 errmsg("column \"%s\" of relation \"%s\" already exists",
7428 colname
, RelationGetRelationName(rel
))));
7435 * Install a column's dependency on its datatype.
7438 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7440 ObjectAddress myself
,
7443 myself
.classId
= RelationRelationId
;
7444 myself
.objectId
= relid
;
7445 myself
.objectSubId
= attnum
;
7446 referenced
.classId
= TypeRelationId
;
7447 referenced
.objectId
= typid
;
7448 referenced
.objectSubId
= 0;
7449 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7453 * Install a column's dependency on its collation.
7456 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7458 ObjectAddress myself
,
7461 /* We know the default collation is pinned, so don't bother recording it */
7462 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7464 myself
.classId
= RelationRelationId
;
7465 myself
.objectId
= relid
;
7466 myself
.objectSubId
= attnum
;
7467 referenced
.classId
= CollationRelationId
;
7468 referenced
.objectId
= collid
;
7469 referenced
.objectSubId
= 0;
7470 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7475 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7477 * Return the address of the modified column. If the column was already
7478 * nullable, InvalidObjectAddress is returned.
7480 static ObjectAddress
7481 ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
7486 Form_pg_attribute attTup
;
7489 ObjectAddress address
;
7493 * lookup the attribute
7495 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7497 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7498 if (!HeapTupleIsValid(tuple
))
7500 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7501 errmsg("column \"%s\" of relation \"%s\" does not exist",
7502 colName
, RelationGetRelationName(rel
))));
7503 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7504 attnum
= attTup
->attnum
;
7505 ObjectAddressSubSet(address
, RelationRelationId
,
7506 RelationGetRelid(rel
), attnum
);
7508 /* If the column is already nullable there's nothing to do. */
7509 if (!attTup
->attnotnull
)
7511 table_close(attr_rel
, RowExclusiveLock
);
7512 return InvalidObjectAddress
;
7515 /* Prevent them from altering a system attribute */
7518 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7519 errmsg("cannot alter system column \"%s\"",
7522 if (attTup
->attidentity
)
7524 (errcode(ERRCODE_SYNTAX_ERROR
),
7525 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7526 colName
, RelationGetRelationName(rel
))));
7529 * It's not OK to remove a constraint only for the parent and leave it in
7530 * the children, so disallow that.
7534 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7536 PartitionDesc partdesc
;
7538 partdesc
= RelationGetPartitionDesc(rel
, true);
7540 if (partdesc
->nparts
> 0)
7542 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7543 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7544 errhint("Do not specify the ONLY keyword."));
7546 else if (rel
->rd_rel
->relhassubclass
&&
7547 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
7550 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7551 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7553 errhint("Do not specify the ONLY keyword."));
7558 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7560 if (rel
->rd_rel
->relispartition
)
7562 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7563 Relation parent
= table_open(parentId
, AccessShareLock
);
7564 TupleDesc tupDesc
= RelationGetDescr(parent
);
7565 AttrNumber parent_attnum
;
7567 parent_attnum
= get_attnum(parentId
, colName
);
7568 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7570 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7571 errmsg("column \"%s\" is marked NOT NULL in parent table",
7573 table_close(parent
, AccessShareLock
);
7577 * Find the constraint that makes this column NOT NULL.
7579 conTup
= findNotNullConstraint(RelationGetRelid(rel
), colName
);
7585 * There's no not-null constraint, so throw an error. If the column
7586 * is in a primary key, we can throw a specific error. Otherwise,
7587 * this is unexpected.
7589 pkcols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_PRIMARY_KEY
);
7590 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
7593 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7594 errmsg("column \"%s\" is in a primary key", colName
));
7596 /* this shouldn't happen */
7597 elog(ERROR
, "could not find not-null constraint on column \"%s\", relation \"%s\"",
7598 colName
, RelationGetRelationName(rel
));
7602 dropconstraint_internal(rel
, conTup
, DROP_RESTRICT
, recurse
, false,
7603 false, &readyRels
, lockmode
);
7605 heap_freetuple(conTup
);
7607 InvokeObjectPostAlterHook(RelationRelationId
,
7608 RelationGetRelid(rel
), attnum
);
7610 table_close(attr_rel
, RowExclusiveLock
);
7616 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7617 * to verify it; recurses to apply the same to children.
7619 * When called to alter an existing table, 'wqueue' must be given so that we can
7620 * queue a check that existing tuples pass the constraint. When called from
7621 * table creation, 'wqueue' should be passed as NULL.
7623 * Returns true if the flag was set in any table, otherwise false.
7626 set_attnotnull(List
**wqueue
, Relation rel
, AttrNumber attnum
, bool recurse
,
7630 Form_pg_attribute attForm
;
7631 bool retval
= false;
7633 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
7634 if (!HeapTupleIsValid(tuple
))
7635 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
7636 attnum
, RelationGetRelid(rel
));
7637 attForm
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7638 if (!attForm
->attnotnull
)
7642 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7644 attForm
->attnotnull
= true;
7645 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7647 table_close(attr_rel
, RowExclusiveLock
);
7650 * And set up for existing values to be checked, unless another
7651 * constraint already proves this.
7653 if (wqueue
&& !NotNullImpliedByRelConstraints(rel
, attForm
))
7655 AlteredTableInfo
*tab
;
7657 tab
= ATGetQueueEntry(wqueue
, rel
);
7658 tab
->verify_new_notnull
= true;
7669 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7670 foreach(lc
, children
)
7672 Oid childrelid
= lfirst_oid(lc
);
7674 AttrNumber childattno
;
7676 /* find_inheritance_children already got lock */
7677 childrel
= table_open(childrelid
, NoLock
);
7678 CheckTableNotInUse(childrel
, "ALTER TABLE");
7680 childattno
= get_attnum(RelationGetRelid(childrel
),
7681 get_attname(RelationGetRelid(rel
), attnum
,
7683 retval
|= set_attnotnull(wqueue
, childrel
, childattno
,
7685 table_close(childrel
, NoLock
);
7693 * ALTER TABLE ALTER COLUMN SET NOT NULL
7695 * Add a not-null constraint to a single table and its children. Returns
7696 * the address of the constraint added to the parent relation, if one gets
7697 * added, or InvalidObjectAddress otherwise.
7699 * We must recurse to child tables during execution, rather than using
7700 * ALTER TABLE's normal prep-time recursion.
7702 static ObjectAddress
7703 ATExecSetNotNull(List
**wqueue
, Relation rel
, char *conName
, char *colName
,
7704 bool recurse
, bool recursing
, List
**readyRels
,
7708 Relation constr_rel
;
7710 SysScanDesc conscan
;
7712 ObjectAddress address
;
7713 Constraint
*constraint
;
7714 CookedConstraint
*ccon
;
7716 bool is_no_inherit
= false;
7720 * In cases of multiple inheritance, we might visit the same child more
7721 * than once. In the topmost call, set up a list that we fill with all
7722 * visited relations, to skip those.
7724 if (readyRels
== NULL
)
7729 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
7730 return InvalidObjectAddress
;
7731 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
7733 /* At top level, permission check was done in ATPrepCmd, else do it */
7736 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7737 Assert(conName
!= NULL
);
7740 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7741 if (attnum
== InvalidAttrNumber
)
7743 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7744 errmsg("column \"%s\" of relation \"%s\" does not exist",
7745 colName
, RelationGetRelationName(rel
))));
7747 /* Prevent them from altering a system attribute */
7750 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7751 errmsg("cannot alter system column \"%s\"",
7754 /* See if there's already a constraint */
7755 constr_rel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
7757 Anum_pg_constraint_conrelid
,
7758 BTEqualStrategyNumber
, F_OIDEQ
,
7759 ObjectIdGetDatum(RelationGetRelid(rel
)));
7760 conscan
= systable_beginscan(constr_rel
, ConstraintRelidTypidNameIndexId
, true,
7763 while (HeapTupleIsValid(tuple
= systable_getnext(conscan
)))
7765 Form_pg_constraint conForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
7766 bool changed
= false;
7769 if (conForm
->contype
!= CONSTRAINT_NOTNULL
)
7772 if (extractNotNullColumn(tuple
) != attnum
)
7775 copytup
= heap_copytuple(tuple
);
7776 conForm
= (Form_pg_constraint
) GETSTRUCT(copytup
);
7779 * If we find an appropriate constraint, we're almost done, but just
7780 * need to change some properties on it: if we're recursing, increment
7781 * coninhcount; if not, set conislocal if not already set.
7785 conForm
->coninhcount
++;
7788 else if (!conForm
->conislocal
)
7790 conForm
->conislocal
= true;
7796 CatalogTupleUpdate(constr_rel
, ©tup
->t_self
, copytup
);
7797 ObjectAddressSet(address
, ConstraintRelationId
, conForm
->oid
);
7800 systable_endscan(conscan
);
7801 table_close(constr_rel
, RowExclusiveLock
);
7806 return InvalidObjectAddress
;
7809 systable_endscan(conscan
);
7810 table_close(constr_rel
, RowExclusiveLock
);
7813 * If we're asked not to recurse, and children exist, raise an error for
7814 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7818 find_inheritance_children(RelationGetRelid(rel
),
7821 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7823 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7824 errmsg("constraint must be added to child tables too"),
7825 errhint("Do not specify the ONLY keyword."));
7827 is_no_inherit
= true;
7831 * No constraint exists; we must add one. First determine a name to use,
7832 * if we haven't already.
7836 Assert(conName
== NULL
);
7837 conName
= ChooseConstraintName(RelationGetRelationName(rel
),
7838 colName
, "not_null",
7839 RelationGetNamespace(rel
),
7842 constraint
= makeNode(Constraint
);
7843 constraint
->contype
= CONSTR_NOTNULL
;
7844 constraint
->conname
= conName
;
7845 constraint
->deferrable
= false;
7846 constraint
->initdeferred
= false;
7847 constraint
->location
= -1;
7848 constraint
->keys
= list_make1(makeString(colName
));
7849 constraint
->is_no_inherit
= is_no_inherit
;
7850 constraint
->inhcount
= recursing
? 1 : 0;
7851 constraint
->skip_validation
= false;
7852 constraint
->initially_valid
= true;
7855 cooked
= AddRelationNewConstraints(rel
, NIL
, list_make1(constraint
),
7856 false, !recursing
, false, NULL
);
7857 ccon
= linitial(cooked
);
7858 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
7860 InvokeObjectPostAlterHook(RelationRelationId
,
7861 RelationGetRelid(rel
), attnum
);
7864 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7865 * recurse, because we're going to do it here.
7867 set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
);
7870 * Recurse to propagate the constraint to children that don't have one.
7877 children
= find_inheritance_children(RelationGetRelid(rel
),
7880 foreach(lc
, children
)
7884 childrel
= table_open(lfirst_oid(lc
), NoLock
);
7886 ATExecSetNotNull(wqueue
, childrel
,
7887 conName
, colName
, recurse
, true,
7888 readyRels
, lockmode
);
7890 table_close(childrel
, NoLock
);
7898 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
7900 * This doesn't exist in the grammar; it's used when creating a
7901 * primary key and the column is not already marked attnotnull.
7903 static ObjectAddress
7904 ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
7905 const char *colName
, LOCKMODE lockmode
)
7908 ObjectAddress address
= InvalidObjectAddress
;
7910 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7911 if (attnum
== InvalidAttrNumber
)
7913 errcode(ERRCODE_UNDEFINED_COLUMN
),
7914 errmsg("column \"%s\" of relation \"%s\" does not exist",
7915 colName
, RelationGetRelationName(rel
)));
7918 * Make the change, if necessary, and only if so report the column as
7921 if (set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
))
7922 ObjectAddressSubSet(address
, RelationRelationId
,
7923 RelationGetRelid(rel
), attnum
);
7929 * NotNullImpliedByRelConstraints
7930 * Does rel's existing constraints imply NOT NULL for the given attribute?
7933 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
7935 NullTest
*nnulltest
= makeNode(NullTest
);
7937 nnulltest
->arg
= (Expr
*) makeVar(1,
7943 nnulltest
->nulltesttype
= IS_NOT_NULL
;
7946 * argisrow = false is correct even for a composite column, because
7947 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7948 * case, just IS DISTINCT FROM NULL.
7950 nnulltest
->argisrow
= false;
7951 nnulltest
->location
= -1;
7953 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
7956 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7957 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
7965 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7967 * Return the address of the affected column.
7969 static ObjectAddress
7970 ATExecColumnDefault(Relation rel
, const char *colName
,
7971 Node
*newDefault
, LOCKMODE lockmode
)
7973 TupleDesc tupdesc
= RelationGetDescr(rel
);
7975 ObjectAddress address
;
7978 * get the number of the attribute
7980 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7981 if (attnum
== InvalidAttrNumber
)
7983 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7984 errmsg("column \"%s\" of relation \"%s\" does not exist",
7985 colName
, RelationGetRelationName(rel
))));
7987 /* Prevent them from altering a system attribute */
7990 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7991 errmsg("cannot alter system column \"%s\"",
7994 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
7996 (errcode(ERRCODE_SYNTAX_ERROR
),
7997 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7998 colName
, RelationGetRelationName(rel
)),
7999 /* translator: %s is an SQL ALTER command */
8000 newDefault
? 0 : errhint("Use %s instead.",
8001 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8003 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
8005 (errcode(ERRCODE_SYNTAX_ERROR
),
8006 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8007 colName
, RelationGetRelationName(rel
)),
8008 newDefault
|| TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
!= ATTRIBUTE_GENERATED_STORED
? 0 :
8009 /* translator: %s is an SQL ALTER command */
8010 errhint("Use %s instead.",
8011 "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION")));
8014 * Remove any old default for the column. We use RESTRICT here for
8015 * safety, but at present we do not expect anything to depend on the
8018 * We treat removing the existing default as an internal operation when it
8019 * is preparatory to adding a new default, but as a user-initiated
8020 * operation when the user asked for a drop.
8022 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8023 newDefault
!= NULL
);
8028 RawColumnDefault
*rawEnt
;
8030 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8031 rawEnt
->attnum
= attnum
;
8032 rawEnt
->raw_default
= newDefault
;
8033 rawEnt
->missingMode
= false;
8034 rawEnt
->generated
= '\0';
8037 * This function is intended for CREATE TABLE, so it processes a
8038 * _list_ of defaults, but we just do one.
8040 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8041 false, true, false, NULL
);
8044 ObjectAddressSubSet(address
, RelationRelationId
,
8045 RelationGetRelid(rel
), attnum
);
8050 * Add a pre-cooked default expression.
8052 * Return the address of the affected column.
8054 static ObjectAddress
8055 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
8058 ObjectAddress address
;
8060 /* We assume no checking is required */
8063 * Remove any old default for the column. We use RESTRICT here for
8064 * safety, but at present we do not expect anything to depend on the
8065 * default. (In ordinary cases, there could not be a default in place
8066 * anyway, but it's possible when combining LIKE with inheritance.)
8068 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8071 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
8073 ObjectAddressSubSet(address
, RelationRelationId
,
8074 RelationGetRelid(rel
), attnum
);
8079 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8081 * Return the address of the affected column.
8083 static ObjectAddress
8084 ATExecAddIdentity(Relation rel
, const char *colName
,
8085 Node
*def
, LOCKMODE lockmode
)
8087 Relation attrelation
;
8089 Form_pg_attribute attTup
;
8091 ObjectAddress address
;
8092 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
8094 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8096 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8097 if (!HeapTupleIsValid(tuple
))
8099 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8100 errmsg("column \"%s\" of relation \"%s\" does not exist",
8101 colName
, RelationGetRelationName(rel
))));
8102 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8103 attnum
= attTup
->attnum
;
8105 /* Can't alter a system attribute */
8108 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8109 errmsg("cannot alter system column \"%s\"",
8113 * Creating a column as identity implies NOT NULL, so adding the identity
8114 * to an existing column that is not NOT NULL would create a state that
8115 * cannot be reproduced without contortions.
8117 if (!attTup
->attnotnull
)
8119 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8120 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8121 colName
, RelationGetRelationName(rel
))));
8123 if (attTup
->attidentity
)
8125 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8126 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8127 colName
, RelationGetRelationName(rel
))));
8129 if (attTup
->atthasdef
)
8131 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8132 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8133 colName
, RelationGetRelationName(rel
))));
8135 attTup
->attidentity
= cdef
->identity
;
8136 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8138 InvokeObjectPostAlterHook(RelationRelationId
,
8139 RelationGetRelid(rel
),
8141 ObjectAddressSubSet(address
, RelationRelationId
,
8142 RelationGetRelid(rel
), attnum
);
8143 heap_freetuple(tuple
);
8145 table_close(attrelation
, RowExclusiveLock
);
8151 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8153 * Return the address of the affected column.
8155 static ObjectAddress
8156 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
, LOCKMODE lockmode
)
8159 DefElem
*generatedEl
= NULL
;
8161 Form_pg_attribute attTup
;
8163 Relation attrelation
;
8164 ObjectAddress address
;
8166 foreach(option
, castNode(List
, def
))
8168 DefElem
*defel
= lfirst_node(DefElem
, option
);
8170 if (strcmp(defel
->defname
, "generated") == 0)
8174 (errcode(ERRCODE_SYNTAX_ERROR
),
8175 errmsg("conflicting or redundant options")));
8176 generatedEl
= defel
;
8179 elog(ERROR
, "option \"%s\" not recognized",
8184 * Even if there is nothing to change here, we run all the checks. There
8185 * will be a subsequent ALTER SEQUENCE that relies on everything being
8189 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8190 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8191 if (!HeapTupleIsValid(tuple
))
8193 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8194 errmsg("column \"%s\" of relation \"%s\" does not exist",
8195 colName
, RelationGetRelationName(rel
))));
8197 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8198 attnum
= attTup
->attnum
;
8202 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8203 errmsg("cannot alter system column \"%s\"",
8206 if (!attTup
->attidentity
)
8208 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8209 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8210 colName
, RelationGetRelationName(rel
))));
8214 attTup
->attidentity
= defGetInt32(generatedEl
);
8215 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8217 InvokeObjectPostAlterHook(RelationRelationId
,
8218 RelationGetRelid(rel
),
8220 ObjectAddressSubSet(address
, RelationRelationId
,
8221 RelationGetRelid(rel
), attnum
);
8224 address
= InvalidObjectAddress
;
8226 heap_freetuple(tuple
);
8227 table_close(attrelation
, RowExclusiveLock
);
8233 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8235 * Return the address of the affected column.
8237 static ObjectAddress
8238 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8241 Form_pg_attribute attTup
;
8243 Relation attrelation
;
8244 ObjectAddress address
;
8246 ObjectAddress seqaddress
;
8248 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8249 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8250 if (!HeapTupleIsValid(tuple
))
8252 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8253 errmsg("column \"%s\" of relation \"%s\" does not exist",
8254 colName
, RelationGetRelationName(rel
))));
8256 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8257 attnum
= attTup
->attnum
;
8261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8262 errmsg("cannot alter system column \"%s\"",
8265 if (!attTup
->attidentity
)
8269 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8270 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8271 colName
, RelationGetRelationName(rel
))));
8275 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8276 colName
, RelationGetRelationName(rel
))));
8277 heap_freetuple(tuple
);
8278 table_close(attrelation
, RowExclusiveLock
);
8279 return InvalidObjectAddress
;
8283 attTup
->attidentity
= '\0';
8284 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8286 InvokeObjectPostAlterHook(RelationRelationId
,
8287 RelationGetRelid(rel
),
8289 ObjectAddressSubSet(address
, RelationRelationId
,
8290 RelationGetRelid(rel
), attnum
);
8291 heap_freetuple(tuple
);
8293 table_close(attrelation
, RowExclusiveLock
);
8295 /* drop the internal sequence */
8296 seqid
= getIdentitySequence(RelationGetRelid(rel
), attnum
, false);
8297 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
8298 RelationRelationId
, DEPENDENCY_INTERNAL
);
8299 CommandCounterIncrement();
8300 seqaddress
.classId
= RelationRelationId
;
8301 seqaddress
.objectId
= seqid
;
8302 seqaddress
.objectSubId
= 0;
8303 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8309 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8312 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8315 * Reject ONLY if there are child tables. We could implement this, but it
8316 * is a bit complicated. GENERATED clauses must be attached to the column
8317 * definition and cannot be added later like DEFAULT, so if a child table
8318 * has a generation expression that the parent does not have, the child
8319 * column will necessarily be an attislocal column. So to implement ONLY
8320 * here, we'd need extra code to update attislocal of the direct child
8321 * tables, somewhat similar to how DROP COLUMN does it, so that the
8322 * resulting state can be properly dumped and restored.
8325 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8327 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8328 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8331 * Cannot drop generation expression from inherited columns.
8336 Form_pg_attribute attTup
;
8338 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8339 if (!HeapTupleIsValid(tuple
))
8341 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8342 errmsg("column \"%s\" of relation \"%s\" does not exist",
8343 cmd
->name
, RelationGetRelationName(rel
))));
8345 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8347 if (attTup
->attinhcount
> 0)
8349 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8350 errmsg("cannot drop generation expression from inherited column")));
8355 * Return the address of the affected column.
8357 static ObjectAddress
8358 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8361 Form_pg_attribute attTup
;
8363 Relation attrelation
;
8365 ObjectAddress address
;
8367 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8368 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8369 if (!HeapTupleIsValid(tuple
))
8371 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8372 errmsg("column \"%s\" of relation \"%s\" does not exist",
8373 colName
, RelationGetRelationName(rel
))));
8375 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8376 attnum
= attTup
->attnum
;
8380 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8381 errmsg("cannot alter system column \"%s\"",
8384 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8389 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8390 colName
, RelationGetRelationName(rel
))));
8394 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8395 colName
, RelationGetRelationName(rel
))));
8396 heap_freetuple(tuple
);
8397 table_close(attrelation
, RowExclusiveLock
);
8398 return InvalidObjectAddress
;
8403 * Mark the column as no longer generated. (The atthasdef flag needs to
8404 * get cleared too, but RemoveAttrDefault will handle that.)
8406 attTup
->attgenerated
= '\0';
8407 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8409 InvokeObjectPostAlterHook(RelationRelationId
,
8410 RelationGetRelid(rel
),
8412 heap_freetuple(tuple
);
8414 table_close(attrelation
, RowExclusiveLock
);
8417 * Drop the dependency records of the GENERATED expression, in particular
8418 * its INTERNAL dependency on the column, which would otherwise cause
8419 * dependency.c to refuse to perform the deletion.
8421 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8422 if (!OidIsValid(attrdefoid
))
8423 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8424 RelationGetRelid(rel
), attnum
);
8425 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8427 /* Make above changes visible */
8428 CommandCounterIncrement();
8431 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8432 * safety, but at present we do not expect anything to depend on the
8435 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8438 ObjectAddressSubSet(address
, RelationRelationId
,
8439 RelationGetRelid(rel
), attnum
);
8444 * ALTER TABLE ALTER COLUMN SET STATISTICS
8446 * Return value is the address of the modified column
8448 static ObjectAddress
8449 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8452 Relation attrelation
;
8454 Form_pg_attribute attrtuple
;
8456 ObjectAddress address
;
8459 * We allow referencing columns by numbers only for indexes, since table
8460 * column numbers could contain gaps if columns are later dropped.
8462 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8463 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8466 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8467 errmsg("cannot refer to non-index column by number")));
8469 Assert(IsA(newValue
, Integer
));
8470 newtarget
= intVal(newValue
);
8473 * Limit target to a sane range
8478 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8479 errmsg("statistics target %d is too low",
8482 else if (newtarget
> MAX_STATISTICS_TARGET
)
8484 newtarget
= MAX_STATISTICS_TARGET
;
8486 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8487 errmsg("lowering statistics target to %d",
8491 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8495 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8497 if (!HeapTupleIsValid(tuple
))
8499 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8500 errmsg("column \"%s\" of relation \"%s\" does not exist",
8501 colName
, RelationGetRelationName(rel
))));
8505 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), colNum
);
8507 if (!HeapTupleIsValid(tuple
))
8509 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8510 errmsg("column number %d of relation \"%s\" does not exist",
8511 colNum
, RelationGetRelationName(rel
))));
8514 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8516 attnum
= attrtuple
->attnum
;
8519 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8520 errmsg("cannot alter system column \"%s\"",
8523 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8524 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8526 if (attnum
> rel
->rd_index
->indnkeyatts
)
8528 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8529 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8530 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8531 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8533 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8534 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8535 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8536 errhint("Alter statistics on table column instead.")));
8539 attrtuple
->attstattarget
= newtarget
;
8541 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8543 InvokeObjectPostAlterHook(RelationRelationId
,
8544 RelationGetRelid(rel
),
8546 ObjectAddressSubSet(address
, RelationRelationId
,
8547 RelationGetRelid(rel
), attnum
);
8548 heap_freetuple(tuple
);
8550 table_close(attrelation
, RowExclusiveLock
);
8556 * Return value is the address of the modified column
8558 static ObjectAddress
8559 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8560 bool isReset
, LOCKMODE lockmode
)
8562 Relation attrelation
;
8565 Form_pg_attribute attrtuple
;
8570 ObjectAddress address
;
8571 Datum repl_val
[Natts_pg_attribute
];
8572 bool repl_null
[Natts_pg_attribute
];
8573 bool repl_repl
[Natts_pg_attribute
];
8575 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8577 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8579 if (!HeapTupleIsValid(tuple
))
8581 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8582 errmsg("column \"%s\" of relation \"%s\" does not exist",
8583 colName
, RelationGetRelationName(rel
))));
8584 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8586 attnum
= attrtuple
->attnum
;
8589 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8590 errmsg("cannot alter system column \"%s\"",
8593 /* Generate new proposed attoptions (text array) */
8594 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8596 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8597 castNode(List
, options
), NULL
, NULL
,
8599 /* Validate new options */
8600 (void) attribute_reloptions(newOptions
, true);
8602 /* Build new tuple. */
8603 memset(repl_null
, false, sizeof(repl_null
));
8604 memset(repl_repl
, false, sizeof(repl_repl
));
8605 if (newOptions
!= (Datum
) 0)
8606 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8608 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8609 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8610 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8611 repl_val
, repl_null
, repl_repl
);
8613 /* Update system catalog. */
8614 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8616 InvokeObjectPostAlterHook(RelationRelationId
,
8617 RelationGetRelid(rel
),
8619 ObjectAddressSubSet(address
, RelationRelationId
,
8620 RelationGetRelid(rel
), attnum
);
8622 heap_freetuple(newtuple
);
8624 ReleaseSysCache(tuple
);
8626 table_close(attrelation
, RowExclusiveLock
);
8632 * Helper function for ATExecSetStorage and ATExecSetCompression
8634 * Set the attstorage and/or attcompression fields for index columns
8635 * associated with the specified table column.
8638 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
8640 bool setstorage
, char newstorage
,
8641 bool setcompression
, char newcompression
,
8646 foreach(lc
, RelationGetIndexList(rel
))
8648 Oid indexoid
= lfirst_oid(lc
);
8650 AttrNumber indattnum
= 0;
8653 indrel
= index_open(indexoid
, lockmode
);
8655 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
8657 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
8666 index_close(indrel
, lockmode
);
8670 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
8672 if (HeapTupleIsValid(tuple
))
8674 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8677 attrtuple
->attstorage
= newstorage
;
8680 attrtuple
->attcompression
= newcompression
;
8682 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8684 InvokeObjectPostAlterHook(RelationRelationId
,
8685 RelationGetRelid(rel
),
8688 heap_freetuple(tuple
);
8691 index_close(indrel
, lockmode
);
8696 * ALTER TABLE ALTER COLUMN SET STORAGE
8698 * Return value is the address of the modified column
8700 static ObjectAddress
8701 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
8703 Relation attrelation
;
8705 Form_pg_attribute attrtuple
;
8707 ObjectAddress address
;
8709 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8711 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8713 if (!HeapTupleIsValid(tuple
))
8715 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8716 errmsg("column \"%s\" of relation \"%s\" does not exist",
8717 colName
, RelationGetRelationName(rel
))));
8718 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8720 attnum
= attrtuple
->attnum
;
8723 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8724 errmsg("cannot alter system column \"%s\"",
8727 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
8729 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8731 InvokeObjectPostAlterHook(RelationRelationId
,
8732 RelationGetRelid(rel
),
8736 * Apply the change to indexes as well (only for simple index columns,
8737 * matching behavior of index.c ConstructTupleDescriptor()).
8739 SetIndexStorageProperties(rel
, attrelation
, attnum
,
8740 true, attrtuple
->attstorage
,
8744 heap_freetuple(tuple
);
8746 table_close(attrelation
, RowExclusiveLock
);
8748 ObjectAddressSubSet(address
, RelationRelationId
,
8749 RelationGetRelid(rel
), attnum
);
8755 * ALTER TABLE DROP COLUMN
8757 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8758 * because we have to decide at runtime whether to recurse or not depending
8759 * on whether attinhcount goes to zero or not. (We can't check this in a
8760 * static pre-pass because it won't handle multiple inheritance situations
8764 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
8765 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
8766 AlterTableUtilityContext
*context
)
8768 if (rel
->rd_rel
->reloftype
&& !recursing
)
8770 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
8771 errmsg("cannot drop column from typed table")));
8773 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
8774 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
8777 cmd
->recurse
= true;
8781 * Drops column 'colName' from relation 'rel' and returns the address of the
8782 * dropped column. The column is also dropped (or marked as no longer
8783 * inherited from relation) from the relation's inheritance children, if any.
8785 * In the recursive invocations for inheritance child relations, instead of
8786 * dropping the column directly (if to be dropped at all), its object address
8787 * is added to 'addrs', which must be non-NULL in such invocations. All
8788 * columns are dropped at the same time after all the children have been
8789 * checked recursively.
8791 static ObjectAddress
8792 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
8793 DropBehavior behavior
,
8794 bool recurse
, bool recursing
,
8795 bool missing_ok
, LOCKMODE lockmode
,
8796 ObjectAddresses
*addrs
)
8799 Form_pg_attribute targetatt
;
8802 ObjectAddress object
;
8805 /* At top level, permission check was done in ATPrepCmd, else do it */
8807 ATSimplePermissions(AT_DropColumn
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
8809 /* Initialize addrs on the first invocation */
8810 Assert(!recursing
|| addrs
!= NULL
);
8812 addrs
= new_object_addresses();
8815 * get the number of the attribute
8817 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8818 if (!HeapTupleIsValid(tuple
))
8823 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8824 errmsg("column \"%s\" of relation \"%s\" does not exist",
8825 colName
, RelationGetRelationName(rel
))));
8830 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8831 colName
, RelationGetRelationName(rel
))));
8832 return InvalidObjectAddress
;
8835 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8837 attnum
= targetatt
->attnum
;
8839 /* Can't drop a system attribute */
8842 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8843 errmsg("cannot drop system column \"%s\"",
8847 * Don't drop inherited columns, unless recursing (presumably from a drop
8848 * of the parent column)
8850 if (targetatt
->attinhcount
> 0 && !recursing
)
8852 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8853 errmsg("cannot drop inherited column \"%s\"",
8857 * Don't drop columns used in the partition key, either. (If we let this
8858 * go through, the key column's dependencies would cause a cascaded drop
8859 * of the whole table, which is surely not what the user expected.)
8861 if (has_partition_attrs(rel
,
8862 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
8865 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8866 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8867 colName
, RelationGetRelationName(rel
))));
8869 ReleaseSysCache(tuple
);
8872 * Propagate to children as appropriate. Unlike most other ALTER
8873 * routines, we have to do this one level of recursion at a time; we can't
8874 * use find_all_inheritors to do it in one pass.
8877 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8885 * In case of a partitioned table, the column must be dropped from the
8886 * partitions as well.
8888 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
8890 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8891 errmsg("cannot drop column from only the partitioned table when partitions exist"),
8892 errhint("Do not specify the ONLY keyword.")));
8894 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
8895 foreach(child
, children
)
8897 Oid childrelid
= lfirst_oid(child
);
8899 Form_pg_attribute childatt
;
8901 /* find_inheritance_children already got lock */
8902 childrel
= table_open(childrelid
, NoLock
);
8903 CheckTableNotInUse(childrel
, "ALTER TABLE");
8905 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
8906 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
8907 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
8908 colName
, childrelid
);
8909 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8911 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
8912 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
8913 childrelid
, colName
);
8918 * If the child column has other definition sources, just
8919 * decrement its inheritance count; if not, recurse to delete
8922 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
8924 /* Time to delete this child column, too */
8925 ATExecDropColumn(wqueue
, childrel
, colName
,
8926 behavior
, true, true,
8927 false, lockmode
, addrs
);
8931 /* Child column must survive my deletion */
8932 childatt
->attinhcount
--;
8934 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
8936 /* Make update visible */
8937 CommandCounterIncrement();
8943 * If we were told to drop ONLY in this table (no recursion),
8944 * we need to mark the inheritors' attributes as locally
8945 * defined rather than inherited.
8947 childatt
->attinhcount
--;
8948 childatt
->attislocal
= true;
8950 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
8952 /* Make update visible */
8953 CommandCounterIncrement();
8956 heap_freetuple(tuple
);
8958 table_close(childrel
, NoLock
);
8960 table_close(attr_rel
, RowExclusiveLock
);
8963 /* Add object to delete */
8964 object
.classId
= RelationRelationId
;
8965 object
.objectId
= RelationGetRelid(rel
);
8966 object
.objectSubId
= attnum
;
8967 add_exact_object_address(&object
, addrs
);
8971 /* Recursion has ended, drop everything that was collected */
8972 performMultipleDeletions(addrs
, behavior
, 0);
8973 free_object_addresses(addrs
);
8980 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
8981 * constraint on its children.
8984 ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
8985 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
8988 List
*newconstrs
= NIL
;
8990 IndexStmt
*indexstmt
;
8992 /* No work if not creating a primary key */
8993 if (!IsA(cmd
->def
, IndexStmt
))
8995 indexstmt
= castNode(IndexStmt
, cmd
->def
);
8996 if (!indexstmt
->primary
)
8999 /* No work if no legacy inheritance children are present */
9000 if (rel
->rd_rel
->relkind
!= RELKIND_RELATION
||
9001 !rel
->rd_rel
->relhassubclass
)
9004 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9006 foreach(lc
, indexstmt
->indexParams
)
9008 IndexElem
*elem
= lfirst_node(IndexElem
, lc
);
9009 Constraint
*nnconstr
;
9011 Assert(elem
->expr
== NULL
);
9013 nnconstr
= makeNode(Constraint
);
9014 nnconstr
->contype
= CONSTR_NOTNULL
;
9015 nnconstr
->conname
= NULL
; /* XXX use PK name? */
9016 nnconstr
->inhcount
= 1;
9017 nnconstr
->deferrable
= false;
9018 nnconstr
->initdeferred
= false;
9019 nnconstr
->location
= -1;
9020 nnconstr
->keys
= list_make1(makeString(elem
->name
));
9021 nnconstr
->skip_validation
= false;
9022 nnconstr
->initially_valid
= true;
9024 newconstrs
= lappend(newconstrs
, nnconstr
);
9027 foreach(lc
, children
)
9029 Oid childrelid
= lfirst_oid(lc
);
9030 Relation childrel
= table_open(childrelid
, NoLock
);
9031 AlterTableCmd
*newcmd
= makeNode(AlterTableCmd
);
9034 newcmd
->subtype
= AT_AddConstraint
;
9035 newcmd
->recurse
= true;
9037 foreach(lc2
, newconstrs
)
9039 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9040 newcmd
->def
= lfirst(lc2
);
9042 ATPrepCmd(wqueue
, childrel
, newcmd
,
9043 true, false, lockmode
, context
);
9046 table_close(childrel
, NoLock
);
9051 * ALTER TABLE ADD INDEX
9053 * There is no such command in the grammar, but parse_utilcmd.c converts
9054 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9055 * us schedule creation of the index at the appropriate time during ALTER.
9057 * Return value is the address of the new index.
9059 static ObjectAddress
9060 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
9061 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9066 ObjectAddress address
;
9068 Assert(IsA(stmt
, IndexStmt
));
9069 Assert(!stmt
->concurrent
);
9071 /* The IndexStmt has already been through transformIndexStmt */
9072 Assert(stmt
->transformed
);
9074 /* suppress schema rights check when rebuilding existing index */
9075 check_rights
= !is_rebuild
;
9076 /* skip index build if phase 3 will do it or we're reusing an old one */
9077 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
9078 /* suppress notices when rebuilding existing index */
9081 address
= DefineIndex(RelationGetRelid(rel
),
9083 InvalidOid
, /* no predefined OID */
9084 InvalidOid
, /* no parent index */
9085 InvalidOid
, /* no parent constraint */
9086 -1, /* total_parts unknown */
9087 true, /* is_alter_table */
9089 false, /* check_not_in_use - we did it already */
9094 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9095 * new index instead of building from scratch. Restore associated fields.
9096 * This may store InvalidSubTransactionId in both fields, in which case
9097 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9098 * this after the CCI that made catalog rows visible to any rebuild. The
9099 * DROP of the old edition of this index will have scheduled the storage
9100 * for deletion at commit, so cancel that pending deletion.
9102 if (RelFileNumberIsValid(stmt
->oldNumber
))
9104 Relation irel
= index_open(address
.objectId
, NoLock
);
9106 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
9107 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
9108 RelationPreserveStorage(irel
->rd_locator
, true);
9109 index_close(irel
, NoLock
);
9116 * ALTER TABLE ADD STATISTICS
9118 * This is no such command in the grammar, but we use this internally to add
9119 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9120 * column type change.
9122 static ObjectAddress
9123 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
9124 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9126 ObjectAddress address
;
9128 Assert(IsA(stmt
, CreateStatsStmt
));
9130 /* The CreateStatsStmt has already been through transformStatsStmt */
9131 Assert(stmt
->transformed
);
9133 address
= CreateStatistics(stmt
);
9139 * ALTER TABLE ADD CONSTRAINT USING INDEX
9141 * Returns the address of the new constraint.
9143 static ObjectAddress
9144 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
9145 IndexStmt
*stmt
, LOCKMODE lockmode
)
9147 Oid index_oid
= stmt
->indexOid
;
9150 IndexInfo
*indexInfo
;
9151 char *constraintName
;
9152 char constraintType
;
9153 ObjectAddress address
;
9156 Assert(IsA(stmt
, IndexStmt
));
9157 Assert(OidIsValid(index_oid
));
9158 Assert(stmt
->isconstraint
);
9161 * Doing this on partitioned tables is not a simple feature to implement,
9162 * so let's punt for now.
9164 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9167 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9169 indexRel
= index_open(index_oid
, AccessShareLock
);
9171 indexName
= pstrdup(RelationGetRelationName(indexRel
));
9173 indexInfo
= BuildIndexInfo(indexRel
);
9175 /* this should have been checked at parse time */
9176 if (!indexInfo
->ii_Unique
)
9177 elog(ERROR
, "index \"%s\" is not unique", indexName
);
9180 * Determine name to assign to constraint. We require a constraint to
9181 * have the same name as the underlying index; therefore, use the index's
9182 * existing name as the default constraint name, and if the user
9183 * explicitly gives some other name for the constraint, rename the index
9186 constraintName
= stmt
->idxname
;
9187 if (constraintName
== NULL
)
9188 constraintName
= indexName
;
9189 else if (strcmp(constraintName
, indexName
) != 0)
9192 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9193 indexName
, constraintName
)));
9194 RenameRelationInternal(index_oid
, constraintName
, false, true);
9197 /* Extra checks needed if making primary key */
9199 index_check_primary_key(rel
, indexInfo
, true, stmt
);
9201 /* Note we currently don't support EXCLUSION constraints here */
9203 constraintType
= CONSTRAINT_PRIMARY
;
9205 constraintType
= CONSTRAINT_UNIQUE
;
9207 /* Create the catalog entries for the constraint */
9208 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
9209 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
9210 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
9211 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
9212 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
9214 address
= index_constraint_create(rel
,
9221 allowSystemTableMods
,
9222 false); /* is_internal */
9224 index_close(indexRel
, NoLock
);
9230 * ALTER TABLE ADD CONSTRAINT
9232 * Return value is the address of the new constraint; if no constraint was
9233 * added, InvalidObjectAddress is returned.
9235 static ObjectAddress
9236 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9237 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
9240 ObjectAddress address
= InvalidObjectAddress
;
9242 Assert(IsA(newConstraint
, Constraint
));
9245 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9246 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9249 switch (newConstraint
->contype
)
9252 case CONSTR_NOTNULL
:
9254 ATAddCheckNNConstraint(wqueue
, tab
, rel
,
9255 newConstraint
, recurse
, false, is_readd
,
9259 case CONSTR_FOREIGN
:
9262 * Assign or validate constraint name
9264 if (newConstraint
->conname
)
9266 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9267 RelationGetRelid(rel
),
9268 newConstraint
->conname
))
9270 (errcode(ERRCODE_DUPLICATE_OBJECT
),
9271 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9272 newConstraint
->conname
,
9273 RelationGetRelationName(rel
))));
9276 newConstraint
->conname
=
9277 ChooseConstraintName(RelationGetRelationName(rel
),
9278 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
9280 RelationGetNamespace(rel
),
9283 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
9290 elog(ERROR
, "unrecognized constraint type: %d",
9291 (int) newConstraint
->contype
);
9298 * Generate the column-name portion of the constraint name for a new foreign
9299 * key given the list of column names that reference the referenced
9300 * table. This will be passed to ChooseConstraintName along with the parent
9301 * table name and the "fkey" suffix.
9303 * We know that less than NAMEDATALEN characters will actually be used, so we
9304 * can truncate the result once we've generated that many.
9306 * XXX see also ChooseExtendedStatisticNameAddition and
9307 * ChooseIndexNameAddition.
9310 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
9312 char buf
[NAMEDATALEN
* 2];
9317 foreach(lc
, colnames
)
9319 const char *name
= strVal(lfirst(lc
));
9322 buf
[buflen
++] = '_'; /* insert _ between names */
9325 * At this point we have buflen <= NAMEDATALEN. name should be less
9326 * than NAMEDATALEN already, but use strlcpy for paranoia.
9328 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
9329 buflen
+= strlen(buf
+ buflen
);
9330 if (buflen
>= NAMEDATALEN
)
9333 return pstrdup(buf
);
9337 * Add a check or not-null constraint to a single table and its children.
9338 * Returns the address of the constraint added to the parent relation,
9339 * if one gets added, or InvalidObjectAddress otherwise.
9341 * Subroutine for ATExecAddConstraint.
9343 * We must recurse to child tables during execution, rather than using
9344 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9345 * constraints *must* be given the same name, else they won't be seen as
9346 * related later. If the user didn't explicitly specify a name, then
9347 * AddRelationNewConstraints would normally assign different names to the
9348 * child constraints. To fix that, we must capture the name assigned at
9349 * the parent table and pass that down.
9351 static ObjectAddress
9352 ATAddCheckNNConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9353 Constraint
*constr
, bool recurse
, bool recursing
,
9354 bool is_readd
, LOCKMODE lockmode
)
9360 ObjectAddress address
= InvalidObjectAddress
;
9362 /* At top level, permission check was done in ATPrepCmd, else do it */
9364 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9367 * Call AddRelationNewConstraints to do the work, making sure it works on
9368 * a copy of the Constraint so transformExpr can't modify the original. It
9369 * returns a list of cooked constraints.
9371 * If the constraint ends up getting merged with a pre-existing one, it's
9372 * omitted from the returned list, which is what we want: we do not need
9373 * to do any validation work. That can only happen at child tables,
9374 * though, since we disallow merging at the top level.
9376 newcons
= AddRelationNewConstraints(rel
, NIL
,
9377 list_make1(copyObject(constr
)),
9378 recursing
|| is_readd
, /* allow_merge */
9379 !recursing
, /* is_local */
9380 is_readd
, /* is_internal */
9381 NULL
); /* queryString not available
9384 /* we don't expect more than one constraint here */
9385 Assert(list_length(newcons
) <= 1);
9387 /* Add each to-be-validated constraint to Phase 3's queue */
9388 foreach(lcon
, newcons
)
9390 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9392 if (!ccon
->skip_validation
&& ccon
->contype
!= CONSTR_NOTNULL
)
9394 NewConstraint
*newcon
;
9396 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9397 newcon
->name
= ccon
->name
;
9398 newcon
->contype
= ccon
->contype
;
9399 newcon
->qual
= ccon
->expr
;
9401 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9404 /* Save the actually assigned name if it was defaulted */
9405 if (constr
->conname
== NULL
)
9406 constr
->conname
= ccon
->name
;
9409 * If adding a not-null constraint, set the pg_attribute flag and tell
9410 * phase 3 to verify existing rows, if needed.
9412 if (constr
->contype
== CONSTR_NOTNULL
)
9413 set_attnotnull(wqueue
, rel
, ccon
->attnum
,
9414 !ccon
->is_no_inherit
, lockmode
);
9416 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9419 /* At this point we must have a locked-down name to use */
9420 Assert(newcons
== NIL
|| constr
->conname
!= NULL
);
9422 /* Advance command counter in case same table is visited multiple times */
9423 CommandCounterIncrement();
9426 * If the constraint got merged with an existing constraint, we're done.
9427 * We mustn't recurse to child tables in this case, because they've
9428 * already got the constraint, and visiting them again would lead to an
9429 * incorrect value for coninhcount.
9435 * If adding a NO INHERIT constraint, no need to find our children.
9437 if (constr
->is_no_inherit
)
9441 * Propagate to children as appropriate. Unlike most other ALTER
9442 * routines, we have to do this one level of recursion at a time; we can't
9443 * use find_all_inheritors to do it in one pass.
9446 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9449 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9450 * constraint creation only if there are no children currently. Error out
9453 if (!recurse
&& children
!= NIL
)
9455 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9456 errmsg("constraint must be added to child tables too")));
9459 * The constraint must appear as inherited in children, so create a
9460 * modified constraint object to use.
9462 constr
= copyObject(constr
);
9463 constr
->inhcount
= 1;
9464 foreach(child
, children
)
9466 Oid childrelid
= lfirst_oid(child
);
9468 AlteredTableInfo
*childtab
;
9470 /* find_inheritance_children already got lock */
9471 childrel
= table_open(childrelid
, NoLock
);
9472 CheckTableNotInUse(childrel
, "ALTER TABLE");
9474 /* Find or create work queue entry for this table */
9475 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9478 * Recurse to child. XXX if we didn't create a constraint on the
9479 * parent because it already existed, and we do create one on a child,
9480 * should we return that child's constraint ObjectAddress here?
9482 ATAddCheckNNConstraint(wqueue
, childtab
, childrel
,
9483 constr
, recurse
, true, is_readd
, lockmode
);
9485 table_close(childrel
, NoLock
);
9492 * Add a foreign-key constraint to a single table; return the new constraint's
9495 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9496 * lock on the rel, and have done appropriate validity checks for it.
9497 * We do permissions checks here, however.
9499 * When the referenced or referencing tables (or both) are partitioned,
9500 * multiple pg_constraint rows are required -- one for each partitioned table
9501 * and each partition on each side (fortunately, not one for every combination
9502 * thereof). We also need action triggers on each leaf partition on the
9503 * referenced side, and check triggers on each leaf partition on the
9506 static ObjectAddress
9507 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9508 Constraint
*fkconstraint
,
9509 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9512 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9513 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9514 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9515 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9516 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9517 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9518 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9519 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9520 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9527 ObjectAddress address
;
9528 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9531 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9532 * delete rows out from under us.
9534 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9535 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9537 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9540 * Validity checks (permission checks wait till we have the column
9543 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9547 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9548 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9549 RelationGetRelationName(rel
),
9550 RelationGetRelationName(pkrel
))));
9551 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9553 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9554 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9555 RelationGetRelationName(rel
),
9556 RelationGetRelationName(pkrel
)),
9557 errdetail("This feature is not yet supported on partitioned tables.")));
9560 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9561 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9563 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9564 errmsg("referenced relation \"%s\" is not a table",
9565 RelationGetRelationName(pkrel
))));
9567 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9569 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9570 errmsg("permission denied: \"%s\" is a system catalog",
9571 RelationGetRelationName(pkrel
))));
9574 * References from permanent or unlogged tables to temp tables, and from
9575 * permanent tables to unlogged tables, are disallowed because the
9576 * referenced data can vanish out from under us. References from temp
9577 * tables to any other table type are also disallowed, because other
9578 * backends might need to run the RI triggers on the perm table, but they
9579 * can't reliably see tuples in the local buffers of other backends.
9581 switch (rel
->rd_rel
->relpersistence
)
9583 case RELPERSISTENCE_PERMANENT
:
9584 if (!RelationIsPermanent(pkrel
))
9586 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9587 errmsg("constraints on permanent tables may reference only permanent tables")));
9589 case RELPERSISTENCE_UNLOGGED
:
9590 if (!RelationIsPermanent(pkrel
)
9591 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9593 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9594 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9596 case RELPERSISTENCE_TEMP
:
9597 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9599 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9600 errmsg("constraints on temporary tables may reference only temporary tables")));
9601 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
9603 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9604 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9609 * Look up the referencing attributes to make sure they exist, and record
9610 * their attnums and type OIDs.
9612 numfks
= transformColumnNameList(RelationGetRelid(rel
),
9613 fkconstraint
->fk_attrs
,
9614 fkattnum
, fktypoid
);
9616 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
9617 fkconstraint
->fk_del_set_cols
,
9618 fkdelsetcols
, NULL
);
9619 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
9620 numfkdelsetcols
, fkdelsetcols
,
9621 fkconstraint
->fk_del_set_cols
);
9624 * If the attribute list for the referenced table was omitted, lookup the
9625 * definition of the primary key and use it. Otherwise, validate the
9626 * supplied attribute list. In either case, discover the index OID and
9627 * index opclasses, and the attnums and type OIDs of the attributes.
9629 if (fkconstraint
->pk_attrs
== NIL
)
9631 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
9632 &fkconstraint
->pk_attrs
,
9638 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
9639 fkconstraint
->pk_attrs
,
9640 pkattnum
, pktypoid
);
9641 /* Look for an index matching the column list */
9642 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
9647 * Now we can check permissions.
9649 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
9652 * Check some things for generated columns.
9654 for (i
= 0; i
< numfks
; i
++)
9656 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
9661 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9663 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9664 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
9665 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
9667 (errcode(ERRCODE_SYNTAX_ERROR
),
9668 errmsg("invalid %s action for foreign key constraint containing generated column",
9670 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
9671 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
9673 (errcode(ERRCODE_SYNTAX_ERROR
),
9674 errmsg("invalid %s action for foreign key constraint containing generated column",
9680 * Look up the equality operators to use in the constraint.
9682 * Note that we have to be careful about the difference between the actual
9683 * PK column type and the opclass' declared input type, which might be
9684 * only binary-compatible with it. The declared opcintype is the right
9685 * thing to probe pg_amop with.
9687 if (numfks
!= numpks
)
9689 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9690 errmsg("number of referencing and referenced columns for foreign key disagree")));
9693 * On the strength of a previous constraint, we might avoid scanning
9694 * tables to validate this one. See below.
9696 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
9697 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
9699 for (i
= 0; i
< numpks
; i
++)
9701 Oid pktype
= pktypoid
[i
];
9702 Oid fktype
= fktypoid
[i
];
9705 Form_pg_opclass cla_tup
;
9715 /* We need several fields out of the pg_opclass entry */
9716 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
9717 if (!HeapTupleIsValid(cla_ht
))
9718 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
9719 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
9720 amid
= cla_tup
->opcmethod
;
9721 opfamily
= cla_tup
->opcfamily
;
9722 opcintype
= cla_tup
->opcintype
;
9723 ReleaseSysCache(cla_ht
);
9726 * Check it's a btree; currently this can never fail since no other
9727 * index AMs support unique indexes. If we ever did have other types
9728 * of unique indexes, we'd need a way to determine which operator
9729 * strategy number is equality. (Is it reasonable to insist that
9730 * every such index AM use btree's number for equality?)
9732 if (amid
!= BTREE_AM_OID
)
9733 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
9734 eqstrategy
= BTEqualStrategyNumber
;
9737 * There had better be a primary equality operator for the index.
9738 * We'll use it for PK = PK comparisons.
9740 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
9743 if (!OidIsValid(ppeqop
))
9744 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
9745 eqstrategy
, opcintype
, opcintype
, opfamily
);
9748 * Are there equality operators that take exactly the FK type? Assume
9749 * we should look through any domain here.
9751 fktyped
= getBaseType(fktype
);
9753 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
9755 if (OidIsValid(pfeqop
))
9757 pfeqop_right
= fktyped
;
9758 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
9763 /* keep compiler quiet */
9764 pfeqop_right
= InvalidOid
;
9765 ffeqop
= InvalidOid
;
9768 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9771 * Otherwise, look for an implicit cast from the FK type to the
9772 * opcintype, and if found, use the primary equality operator.
9773 * This is a bit tricky because opcintype might be a polymorphic
9774 * type such as ANYARRAY or ANYENUM; so what we have to test is
9775 * whether the two actual column types can be concurrently cast to
9776 * that type. (Otherwise, we'd fail to reject combinations such
9777 * as int[] and point[].)
9779 Oid input_typeids
[2];
9780 Oid target_typeids
[2];
9782 input_typeids
[0] = pktype
;
9783 input_typeids
[1] = fktype
;
9784 target_typeids
[0] = opcintype
;
9785 target_typeids
[1] = opcintype
;
9786 if (can_coerce_type(2, input_typeids
, target_typeids
,
9789 pfeqop
= ffeqop
= ppeqop
;
9790 pfeqop_right
= opcintype
;
9794 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9796 (errcode(ERRCODE_DATATYPE_MISMATCH
),
9797 errmsg("foreign key constraint \"%s\" cannot be implemented",
9798 fkconstraint
->conname
),
9799 errdetail("Key columns \"%s\" and \"%s\" "
9800 "are of incompatible types: %s and %s.",
9801 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
9802 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
9803 format_type_be(fktype
),
9804 format_type_be(pktype
))));
9809 * When a pfeqop changes, revalidate the constraint. We could
9810 * permit intra-opfamily changes, but that adds subtle complexity
9811 * without any concrete benefit for core types. We need not
9812 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9814 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
9815 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
9822 CoercionPathType old_pathtype
;
9823 CoercionPathType new_pathtype
;
9826 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
9830 * Identify coercion pathways from each of the old and new FK-side
9831 * column types to the right (foreign) operand type of the pfeqop.
9832 * We may assume that pg_constraint.conkey is not changing.
9834 old_fktype
= attr
->atttypid
;
9835 new_fktype
= fktype
;
9836 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
9838 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
9842 * Upon a change to the cast from the FK column to its pfeqop
9843 * operand, revalidate the constraint. For this evaluation, a
9844 * binary coercion cast is equivalent to no cast at all. While
9845 * type implementors should design implicit casts with an eye
9846 * toward consistency of operations like equality, we cannot
9847 * assume here that they have done so.
9849 * A function with a polymorphic argument could change behavior
9850 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9851 * when the cast destination is polymorphic, we only avoid
9852 * revalidation if the input type has not changed at all. Given
9853 * just the core data types and operator classes, this requirement
9854 * prevents no would-be optimizations.
9856 * If the cast converts from a base type to a domain thereon, then
9857 * that domain type must be the opcintype of the unique index.
9858 * Necessarily, the primary key column must then be of the domain
9859 * type. Since the constraint was previously valid, all values on
9860 * the foreign side necessarily exist on the primary side and in
9861 * turn conform to the domain. Consequently, we need not treat
9862 * domains specially here.
9864 * Since we require that all collations share the same notion of
9865 * equality (which they do, because texteq reduces to bitwise
9866 * equality), we don't compare collation here.
9868 * We need not directly consider the PK type. It's necessarily
9869 * binary coercible to the opcintype of the unique index column,
9870 * and ri_triggers.c will only deal with PK datums in terms of
9871 * that opcintype. Changing the opcintype also changes pfeqop.
9873 old_check_ok
= (new_pathtype
== old_pathtype
&&
9874 new_castfunc
== old_castfunc
&&
9875 (!IsPolymorphicType(pfeqop_right
) ||
9876 new_fktype
== old_fktype
));
9879 pfeqoperators
[i
] = pfeqop
;
9880 ppeqoperators
[i
] = ppeqop
;
9881 ffeqoperators
[i
] = ffeqop
;
9885 * Create all the constraint and trigger objects, recursing to partitions
9886 * as necessary. First handle the referenced side.
9888 address
= addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, pkrel
,
9890 InvalidOid
, /* no parent constraint */
9900 InvalidOid
, InvalidOid
);
9902 /* Now handle the referencing side. */
9903 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
9916 InvalidOid
, InvalidOid
);
9919 * Done. Close pk table, but keep lock until we've committed.
9921 table_close(pkrel
, NoLock
);
9927 * validateFkOnDeleteSetColumns
9928 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9929 * column lists are valid.
9932 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
9933 int numfksetcols
, const int16
*fksetcolsattnums
,
9936 for (int i
= 0; i
< numfksetcols
; i
++)
9938 int16 setcol_attnum
= fksetcolsattnums
[i
];
9941 for (int j
= 0; j
< numfks
; j
++)
9943 if (fkattnums
[j
] == setcol_attnum
)
9952 char *col
= strVal(list_nth(fksetcols
, i
));
9955 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
9956 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
9962 * addFkRecurseReferenced
9963 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9964 * side of the constraint
9966 * Create pg_constraint rows for the referenced side of the constraint,
9967 * referencing the parent of the referencing side; also create action triggers
9968 * on leaf partitions. If the table is partitioned, recurse to handle each
9971 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9972 * of an ALTER TABLE sequence.
9973 * fkconstraint is the constraint being added.
9974 * rel is the root referencing relation.
9975 * pkrel is the referenced relation; might be a partition, if recursing.
9976 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9977 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
9978 * top-level constraint.
9979 * numfks is the number of columns in the foreign key
9980 * pkattnum is the attnum array of referenced attributes.
9981 * fkattnum is the attnum array of referencing attributes.
9982 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9984 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9985 * NULL/DEFAULT clause
9986 * pf/pp/ffeqoperators are OID array of operators between columns.
9987 * old_check_ok signals that this constraint replaces an existing one that
9988 * was already validated (thus this one doesn't need validation).
9989 * parentDelTrigger and parentUpdTrigger, when being recursively called on
9990 * a partition, are the OIDs of the parent action triggers for DELETE and
9991 * UPDATE respectively.
9993 static ObjectAddress
9994 addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
9995 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
9997 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
9998 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
9999 int numfkdelsetcols
, int16
*fkdelsetcols
,
10001 Oid parentDelTrigger
, Oid parentUpdTrigger
)
10003 ObjectAddress address
;
10009 Oid deleteTriggerOid
,
10013 * Verify relkind for each referenced partition. At the top level, this
10014 * is redundant with a previous check, but we need it when recursing.
10016 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
10017 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
10019 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10020 errmsg("referenced relation \"%s\" is not a table",
10021 RelationGetRelationName(pkrel
))));
10024 * Caller supplies us with a constraint name; however, it may be used in
10025 * this partition, so come up with a different one in that case.
10027 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10028 RelationGetRelid(rel
),
10029 fkconstraint
->conname
))
10030 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
10031 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10033 RelationGetNamespace(rel
), NIL
);
10035 conname
= fkconstraint
->conname
;
10037 if (OidIsValid(parentConstr
))
10039 conislocal
= false;
10041 connoinherit
= false;
10049 * always inherit for partitioned tables, never for legacy inheritance
10051 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
10055 * Record the FK constraint in pg_constraint.
10057 constrOid
= CreateConstraintEntry(conname
,
10058 RelationGetNamespace(rel
),
10059 CONSTRAINT_FOREIGN
,
10060 fkconstraint
->deferrable
,
10061 fkconstraint
->initdeferred
,
10062 fkconstraint
->initially_valid
,
10064 RelationGetRelid(rel
),
10068 InvalidOid
, /* not a domain constraint */
10070 RelationGetRelid(pkrel
),
10076 fkconstraint
->fk_upd_action
,
10077 fkconstraint
->fk_del_action
,
10080 fkconstraint
->fk_matchtype
,
10081 NULL
, /* no exclusion constraint */
10082 NULL
, /* no check constraint */
10084 conislocal
, /* islocal */
10085 coninhcount
, /* inhcount */
10086 connoinherit
, /* conNoInherit */
10087 false); /* is_internal */
10089 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10092 * Mark the child constraint as part of the parent constraint; it must not
10093 * be dropped on its own. (This constraint is deleted when the partition
10094 * is detached, but a special check needs to occur that the partition
10095 * contains no referenced values.)
10097 if (OidIsValid(parentConstr
))
10099 ObjectAddress referenced
;
10101 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10102 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
10105 /* make new constraint visible, in case we add more */
10106 CommandCounterIncrement();
10109 * Create the action triggers that enforce the constraint.
10111 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
10113 constrOid
, indexOid
,
10114 parentDelTrigger
, parentUpdTrigger
,
10115 &deleteTriggerOid
, &updateTriggerOid
);
10118 * If the referenced table is partitioned, recurse on ourselves to handle
10119 * each partition. We need one pg_constraint row created for each
10120 * partition in addition to the pg_constraint row for the parent table.
10122 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10124 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
10126 for (int i
= 0; i
< pd
->nparts
; i
++)
10130 AttrNumber
*mapped_pkattnum
;
10133 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
10136 * Map the attribute numbers in the referenced side of the FK
10137 * definition to match the partition's column layout.
10139 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
10140 RelationGetDescr(pkrel
),
10144 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
10145 for (int j
= 0; j
< numfks
; j
++)
10146 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
10149 mapped_pkattnum
= pkattnum
;
10152 partIndexId
= index_get_partition(partRel
, indexOid
);
10153 if (!OidIsValid(partIndexId
))
10154 elog(ERROR
, "index for %u not found in partition %s",
10155 indexOid
, RelationGetRelationName(partRel
));
10156 addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, partRel
,
10157 partIndexId
, constrOid
, numfks
,
10158 mapped_pkattnum
, fkattnum
,
10159 pfeqoperators
, ppeqoperators
, ffeqoperators
,
10160 numfkdelsetcols
, fkdelsetcols
,
10162 deleteTriggerOid
, updateTriggerOid
);
10164 /* Done -- clean up (but keep the lock) */
10165 table_close(partRel
, NoLock
);
10168 pfree(mapped_pkattnum
);
10178 * addFkRecurseReferencing
10179 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10181 * If the referencing relation is a plain relation, create the necessary check
10182 * triggers that implement the constraint, and set up for Phase 3 constraint
10183 * verification. If the referencing relation is a partitioned table, then
10184 * we create a pg_constraint row for it and recurse on this routine for each
10187 * We assume that the referenced relation is locked against concurrent
10188 * deletions. If it's a partitioned relation, every partition must be so
10191 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10192 * of an ALTER TABLE sequence.
10193 * fkconstraint is the constraint being added.
10194 * rel is the referencing relation; might be a partition, if recursing.
10195 * pkrel is the root referenced relation.
10196 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10197 * parentConstr is the OID of the parent constraint (there is always one).
10198 * numfks is the number of columns in the foreign key
10199 * pkattnum is the attnum array of referenced attributes.
10200 * fkattnum is the attnum array of referencing attributes.
10201 * pf/pp/ffeqoperators are OID array of operators between columns.
10202 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10204 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10205 * NULL/DEFAULT clause
10206 * old_check_ok signals that this constraint replaces an existing one that
10207 * was already validated (thus this one doesn't need validation).
10208 * lockmode is the lockmode to acquire on partitions when recursing.
10209 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10210 * a partition, are the OIDs of the parent check triggers for INSERT and
10211 * UPDATE respectively.
10214 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10215 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10216 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
10217 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10218 int numfkdelsetcols
, int16
*fkdelsetcols
,
10219 bool old_check_ok
, LOCKMODE lockmode
,
10220 Oid parentInsTrigger
, Oid parentUpdTrigger
)
10222 Oid insertTriggerOid
,
10225 Assert(OidIsValid(parentConstr
));
10227 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10229 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10230 errmsg("foreign key constraints are not supported on foreign tables")));
10233 * Add the check triggers to it and, if necessary, schedule it to be
10234 * checked in Phase 3.
10236 * If the relation is partitioned, drill down to do it to its partitions.
10238 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
10239 RelationGetRelid(pkrel
),
10243 parentInsTrigger
, parentUpdTrigger
,
10244 &insertTriggerOid
, &updateTriggerOid
);
10246 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
10249 * Tell Phase 3 to check that the constraint is satisfied by existing
10250 * rows. We can skip this during table creation, when requested
10251 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10252 * and when we're recreating a constraint following a SET DATA TYPE
10253 * operation that did not impugn its validity.
10255 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
10257 NewConstraint
*newcon
;
10258 AlteredTableInfo
*tab
;
10260 tab
= ATGetQueueEntry(wqueue
, rel
);
10262 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
10263 newcon
->name
= get_constraint_name(parentConstr
);
10264 newcon
->contype
= CONSTR_FOREIGN
;
10265 newcon
->refrelid
= RelationGetRelid(pkrel
);
10266 newcon
->refindid
= indexOid
;
10267 newcon
->conid
= parentConstr
;
10268 newcon
->qual
= (Node
*) fkconstraint
;
10270 tab
->constraints
= lappend(tab
->constraints
, newcon
);
10273 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10275 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
10279 * Triggers of the foreign keys will be manipulated a bunch of times
10280 * in the loop below. To avoid repeatedly opening/closing the trigger
10281 * catalog relation, we open it here and pass it to the subroutines
10284 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10287 * Recurse to take appropriate action on each partition; either we
10288 * find an existing constraint to reparent to ours, or we create a new
10291 for (int i
= 0; i
< pd
->nparts
; i
++)
10293 Oid partitionId
= pd
->oids
[i
];
10294 Relation partition
= table_open(partitionId
, lockmode
);
10297 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
10301 ObjectAddress address
,
10305 CheckTableNotInUse(partition
, "ALTER TABLE");
10307 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
10308 RelationGetDescr(rel
),
10310 for (int j
= 0; j
< numfks
; j
++)
10311 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
10313 /* Check whether an existing constraint can be repurposed */
10314 partFKs
= copyObject(RelationGetFKeyList(partition
));
10316 foreach(cell
, partFKs
)
10318 ForeignKeyCacheInfo
*fk
;
10320 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
10321 if (tryAttachPartitionForeignKey(fk
,
10338 table_close(partition
, NoLock
);
10343 * No luck finding a good constraint to reuse; create our own.
10345 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10346 RelationGetRelid(partition
),
10347 fkconstraint
->conname
))
10348 conname
= ChooseConstraintName(RelationGetRelationName(partition
),
10349 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10351 RelationGetNamespace(partition
), NIL
);
10353 conname
= fkconstraint
->conname
;
10355 CreateConstraintEntry(conname
,
10356 RelationGetNamespace(partition
),
10357 CONSTRAINT_FOREIGN
,
10358 fkconstraint
->deferrable
,
10359 fkconstraint
->initdeferred
,
10360 fkconstraint
->initially_valid
,
10368 RelationGetRelid(pkrel
),
10374 fkconstraint
->fk_upd_action
,
10375 fkconstraint
->fk_del_action
,
10378 fkconstraint
->fk_matchtype
,
10388 * Give this constraint partition-type dependencies on the parent
10389 * constraint as well as the table.
10391 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10392 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10393 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10394 ObjectAddressSet(referenced
, RelationRelationId
, partitionId
);
10395 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10397 /* Make all this visible before recursing */
10398 CommandCounterIncrement();
10400 /* call ourselves to finalize the creation and we're done */
10401 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10417 table_close(partition
, NoLock
);
10420 table_close(trigrel
, RowExclusiveLock
);
10425 * CloneForeignKeyConstraints
10426 * Clone foreign keys from a partitioned table to a newly acquired
10429 * partitionRel is a partition of parentRel, so we can be certain that it has
10430 * the same columns with the same datatypes. The columns may be in different
10433 * wqueue must be passed to set up phase 3 constraint checking, unless the
10434 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10438 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10439 Relation partitionRel
)
10441 /* This only works for declarative partitioning */
10442 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10445 * Clone constraints for which the parent is on the referenced side.
10447 CloneFkReferenced(parentRel
, partitionRel
);
10450 * Now clone constraints where the parent is on the referencing side.
10452 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10456 * CloneFkReferenced
10457 * Subroutine for CloneForeignKeyConstraints
10459 * Find all the FKs that have the parent relation on the referenced side;
10460 * clone those constraints to the given partition. This is to be called
10461 * when the partition is being created or attached.
10463 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10465 * This recurses to partitions, if the relation being attached is partitioned.
10466 * Recursion is done by calling addFkRecurseReferenced.
10469 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10471 Relation pg_constraint
;
10475 ScanKeyData key
[2];
10481 * Search for any constraints where this partition's parent is in the
10482 * referenced side. However, we must not clone any constraint whose
10483 * parent constraint is also going to be cloned, to avoid duplicates. So
10484 * do it in two steps: first construct the list of constraints to clone,
10485 * then go over that list cloning those whose parents are not in the list.
10486 * (We must not rely on the parent being seen first, since the catalog
10487 * scan could return children first.)
10489 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
10490 ScanKeyInit(&key
[0],
10491 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
10492 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
10493 ScanKeyInit(&key
[1],
10494 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
10495 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
10496 /* This is a seqscan, as we don't have a usable index ... */
10497 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
10499 while ((tuple
= systable_getnext(scan
)) != NULL
)
10501 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10503 clone
= lappend_oid(clone
, constrForm
->oid
);
10505 systable_endscan(scan
);
10506 table_close(pg_constraint
, RowShareLock
);
10509 * Triggers of the foreign keys will be manipulated a bunch of times in
10510 * the loop below. To avoid repeatedly opening/closing the trigger
10511 * catalog relation, we open it here and pass it to the subroutines called
10514 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10516 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
10517 RelationGetDescr(parentRel
),
10519 foreach(cell
, clone
)
10521 Oid constrOid
= lfirst_oid(cell
);
10522 Form_pg_constraint constrForm
;
10527 AttrNumber conkey
[INDEX_MAX_KEYS
];
10528 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
10529 AttrNumber confkey
[INDEX_MAX_KEYS
];
10530 Oid conpfeqop
[INDEX_MAX_KEYS
];
10531 Oid conppeqop
[INDEX_MAX_KEYS
];
10532 Oid conffeqop
[INDEX_MAX_KEYS
];
10533 int numfkdelsetcols
;
10534 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10535 Constraint
*fkconstraint
;
10536 Oid deleteTriggerOid
,
10539 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
10540 if (!HeapTupleIsValid(tuple
))
10541 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
10542 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10545 * As explained above: don't try to clone a constraint for which we're
10546 * going to clone the parent.
10548 if (list_member_oid(clone
, constrForm
->conparentid
))
10550 ReleaseSysCache(tuple
);
10555 * Don't clone self-referencing foreign keys, which can be in the
10556 * partitioned table or in the partition-to-be.
10558 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
10559 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
10561 ReleaseSysCache(tuple
);
10566 * Because we're only expanding the key space at the referenced side,
10567 * we don't need to prevent any operation in the referencing table, so
10568 * AccessShareLock suffices (assumes that dropping the constraint
10571 fkRel
= table_open(constrForm
->conrelid
, AccessShareLock
);
10573 indexOid
= constrForm
->conindid
;
10574 DeconstructFkConstraintRow(tuple
,
10584 for (int i
= 0; i
< numfks
; i
++)
10585 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
10587 fkconstraint
= makeNode(Constraint
);
10588 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10589 fkconstraint
->conname
= NameStr(constrForm
->conname
);
10590 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10591 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10592 fkconstraint
->location
= -1;
10593 fkconstraint
->pktable
= NULL
;
10594 /* ->fk_attrs determined below */
10595 fkconstraint
->pk_attrs
= NIL
;
10596 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10597 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10598 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10599 fkconstraint
->fk_del_set_cols
= NIL
;
10600 fkconstraint
->old_conpfeqop
= NIL
;
10601 fkconstraint
->old_pktable_oid
= InvalidOid
;
10602 fkconstraint
->skip_validation
= false;
10603 fkconstraint
->initially_valid
= true;
10605 /* set up colnames that are used to generate the constraint name */
10606 for (int i
= 0; i
< numfks
; i
++)
10608 Form_pg_attribute att
;
10610 att
= TupleDescAttr(RelationGetDescr(fkRel
),
10612 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10613 makeString(NameStr(att
->attname
)));
10617 * Add the new foreign key constraint pointing to the new partition.
10618 * Because this new partition appears in the referenced side of the
10619 * constraint, we don't need to set up for Phase 3 check.
10621 partIndexId
= index_get_partition(partitionRel
, indexOid
);
10622 if (!OidIsValid(partIndexId
))
10623 elog(ERROR
, "index for %u not found in partition %s",
10624 indexOid
, RelationGetRelationName(partitionRel
));
10627 * Get the "action" triggers belonging to the constraint to pass as
10628 * parent OIDs for similar triggers that will be created on the
10629 * partition in addFkRecurseReferenced().
10631 GetForeignKeyActionTriggers(trigrel
, constrOid
,
10632 constrForm
->confrelid
, constrForm
->conrelid
,
10633 &deleteTriggerOid
, &updateTriggerOid
);
10635 addFkRecurseReferenced(NULL
,
10653 table_close(fkRel
, NoLock
);
10654 ReleaseSysCache(tuple
);
10657 table_close(trigrel
, RowExclusiveLock
);
10661 * CloneFkReferencing
10662 * Subroutine for CloneForeignKeyConstraints
10664 * For each FK constraint of the parent relation in the given list, find an
10665 * equivalent constraint in its partition relation that can be reparented;
10666 * if one cannot be found, create a new constraint in the partition as its
10669 * If wqueue is given, it is used to set up phase-3 verification for each
10670 * cloned constraint; if omitted, we assume that such verification is not
10671 * needed (example: the partition is being created anew).
10674 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
10682 /* obtain a list of constraints that we need to clone */
10683 foreach(cell
, RelationGetFKeyList(parentRel
))
10685 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
10687 clone
= lappend_oid(clone
, fk
->conoid
);
10691 * Silently do nothing if there's nothing to do. In particular, this
10692 * avoids throwing a spurious error for foreign tables.
10697 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10699 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10700 errmsg("foreign key constraints are not supported on foreign tables")));
10703 * Triggers of the foreign keys will be manipulated a bunch of times in
10704 * the loop below. To avoid repeatedly opening/closing the trigger
10705 * catalog relation, we open it here and pass it to the subroutines called
10708 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10711 * The constraint key may differ, if the columns in the partition are
10712 * different. This map is used to convert them.
10714 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
10715 RelationGetDescr(parentRel
),
10718 partFKs
= copyObject(RelationGetFKeyList(partRel
));
10720 foreach(cell
, clone
)
10722 Oid parentConstrOid
= lfirst_oid(cell
);
10723 Form_pg_constraint constrForm
;
10727 AttrNumber conkey
[INDEX_MAX_KEYS
];
10728 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
10729 AttrNumber confkey
[INDEX_MAX_KEYS
];
10730 Oid conpfeqop
[INDEX_MAX_KEYS
];
10731 Oid conppeqop
[INDEX_MAX_KEYS
];
10732 Oid conffeqop
[INDEX_MAX_KEYS
];
10733 int numfkdelsetcols
;
10734 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10735 Constraint
*fkconstraint
;
10739 ObjectAddress address
,
10742 Oid insertTriggerOid
,
10745 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
10746 if (!HeapTupleIsValid(tuple
))
10747 elog(ERROR
, "cache lookup failed for constraint %u",
10749 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10751 /* Don't clone constraints whose parents are being cloned */
10752 if (list_member_oid(clone
, constrForm
->conparentid
))
10754 ReleaseSysCache(tuple
);
10759 * Need to prevent concurrent deletions. If pkrel is a partitioned
10760 * relation, that means to lock all partitions.
10762 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
10763 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10764 (void) find_all_inheritors(RelationGetRelid(pkrel
),
10765 ShareRowExclusiveLock
, NULL
);
10767 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
10768 conpfeqop
, conppeqop
, conffeqop
,
10769 &numfkdelsetcols
, confdelsetcols
);
10770 for (int i
= 0; i
< numfks
; i
++)
10771 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
10774 * Get the "check" triggers belonging to the constraint to pass as
10775 * parent OIDs for similar triggers that will be created on the
10776 * partition in addFkRecurseReferencing(). They are also passed to
10777 * tryAttachPartitionForeignKey() below to simply assign as parents to
10778 * the partition's existing "check" triggers, that is, if the
10779 * corresponding constraints is deemed attachable to the parent
10782 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
10783 constrForm
->confrelid
, constrForm
->conrelid
,
10784 &insertTriggerOid
, &updateTriggerOid
);
10787 * Before creating a new constraint, see whether any existing FKs are
10788 * fit for the purpose. If one is, attach the parent constraint to
10789 * it, and don't clone anything. This way we avoid the expensive
10790 * verification step and don't end up with a duplicate FK, and we
10791 * don't need to recurse to partitions for this constraint.
10794 foreach(lc
, partFKs
)
10796 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
10798 if (tryAttachPartitionForeignKey(fk
,
10799 RelationGetRelid(partRel
),
10810 table_close(pkrel
, NoLock
);
10816 ReleaseSysCache(tuple
);
10820 /* No dice. Set up to create our own constraint */
10821 fkconstraint
= makeNode(Constraint
);
10822 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10823 /* ->conname determined below */
10824 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10825 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10826 fkconstraint
->location
= -1;
10827 fkconstraint
->pktable
= NULL
;
10828 /* ->fk_attrs determined below */
10829 fkconstraint
->pk_attrs
= NIL
;
10830 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10831 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10832 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10833 fkconstraint
->fk_del_set_cols
= NIL
;
10834 fkconstraint
->old_conpfeqop
= NIL
;
10835 fkconstraint
->old_pktable_oid
= InvalidOid
;
10836 fkconstraint
->skip_validation
= false;
10837 fkconstraint
->initially_valid
= true;
10838 for (int i
= 0; i
< numfks
; i
++)
10840 Form_pg_attribute att
;
10842 att
= TupleDescAttr(RelationGetDescr(partRel
),
10843 mapped_conkey
[i
] - 1);
10844 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10845 makeString(NameStr(att
->attname
)));
10847 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10848 RelationGetRelid(partRel
),
10849 NameStr(constrForm
->conname
)))
10850 fkconstraint
->conname
=
10851 ChooseConstraintName(RelationGetRelationName(partRel
),
10852 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10854 RelationGetNamespace(partRel
), NIL
);
10856 fkconstraint
->conname
= pstrdup(NameStr(constrForm
->conname
));
10858 indexOid
= constrForm
->conindid
;
10860 CreateConstraintEntry(fkconstraint
->conname
,
10861 constrForm
->connamespace
,
10862 CONSTRAINT_FOREIGN
,
10863 fkconstraint
->deferrable
,
10864 fkconstraint
->initdeferred
,
10865 constrForm
->convalidated
,
10867 RelationGetRelid(partRel
),
10871 InvalidOid
, /* not a domain constraint */
10873 constrForm
->confrelid
, /* same foreign rel */
10879 fkconstraint
->fk_upd_action
,
10880 fkconstraint
->fk_del_action
,
10883 fkconstraint
->fk_matchtype
,
10887 false, /* islocal */
10889 false, /* conNoInherit */
10892 /* Set up partition dependencies for the new constraint */
10893 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10894 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstrOid
);
10895 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10896 ObjectAddressSet(referenced
, RelationRelationId
,
10897 RelationGetRelid(partRel
));
10898 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10900 /* Done with the cloned constraint's tuple */
10901 ReleaseSysCache(tuple
);
10903 /* Make all this visible before recursing */
10904 CommandCounterIncrement();
10906 addFkRecurseReferencing(wqueue
,
10920 false, /* no old check exists */
10921 AccessExclusiveLock
,
10924 table_close(pkrel
, NoLock
);
10927 table_close(trigrel
, RowExclusiveLock
);
10931 * When the parent of a partition receives [the referencing side of] a foreign
10932 * key, we must propagate that foreign key to the partition. However, the
10933 * partition might already have an equivalent foreign key; this routine
10934 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10935 * by the other parameters. If they are equivalent, create the link between
10936 * the two constraints and return true.
10938 * If the given FK does not match the one defined by rest of the params,
10942 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
10944 Oid parentConstrOid
,
10946 AttrNumber
*mapped_conkey
,
10947 AttrNumber
*confkey
,
10949 Oid parentInsTrigger
,
10950 Oid parentUpdTrigger
,
10953 HeapTuple parentConstrTup
;
10954 Form_pg_constraint parentConstr
;
10955 HeapTuple partcontup
;
10956 Form_pg_constraint partConstr
;
10960 Oid insertTriggerOid
,
10963 parentConstrTup
= SearchSysCache1(CONSTROID
,
10964 ObjectIdGetDatum(parentConstrOid
));
10965 if (!HeapTupleIsValid(parentConstrTup
))
10966 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
10967 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
10970 * Do some quick & easy initial checks. If any of these fail, we cannot
10971 * use this constraint.
10973 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
10975 ReleaseSysCache(parentConstrTup
);
10978 for (int i
= 0; i
< numfks
; i
++)
10980 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
10981 fk
->confkey
[i
] != confkey
[i
] ||
10982 fk
->conpfeqop
[i
] != conpfeqop
[i
])
10984 ReleaseSysCache(parentConstrTup
);
10990 * Looks good so far; do some more extensive checks. Presumably the check
10991 * for 'convalidated' could be dropped, since we don't really care about
10992 * that, but let's be careful for now.
10994 partcontup
= SearchSysCache1(CONSTROID
,
10995 ObjectIdGetDatum(fk
->conoid
));
10996 if (!HeapTupleIsValid(partcontup
))
10997 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
10998 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
10999 if (OidIsValid(partConstr
->conparentid
) ||
11000 !partConstr
->convalidated
||
11001 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
11002 partConstr
->condeferred
!= parentConstr
->condeferred
||
11003 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
11004 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
11005 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
11007 ReleaseSysCache(parentConstrTup
);
11008 ReleaseSysCache(partcontup
);
11012 ReleaseSysCache(partcontup
);
11013 ReleaseSysCache(parentConstrTup
);
11016 * Looks good! Attach this constraint. The action triggers in the new
11017 * partition become redundant -- the parent table already has equivalent
11018 * ones, and those will be able to reach the partition. Remove the ones
11019 * in the partition. We identify them because they have our constraint
11020 * OID, as well as being on the referenced rel.
11023 Anum_pg_trigger_tgconstraint
,
11024 BTEqualStrategyNumber
, F_OIDEQ
,
11025 ObjectIdGetDatum(fk
->conoid
));
11026 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11028 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11030 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11031 ObjectAddress trigger
;
11033 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
11035 if (trgform
->tgrelid
!= fk
->confrelid
)
11039 * The constraint is originally set up to contain this trigger as an
11040 * implementation object, so there's a dependency record that links
11041 * the two; however, since the trigger is no longer needed, we remove
11042 * the dependency link in order to be able to drop the trigger while
11043 * keeping the constraint intact.
11045 deleteDependencyRecordsFor(TriggerRelationId
,
11048 /* make dependency deletion visible to performDeletion */
11049 CommandCounterIncrement();
11050 ObjectAddressSet(trigger
, TriggerRelationId
,
11052 performDeletion(&trigger
, DROP_RESTRICT
, 0);
11053 /* make trigger drop visible, in case the loop iterates */
11054 CommandCounterIncrement();
11057 systable_endscan(scan
);
11059 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
11062 * Like the constraint, attach partition's "check" triggers to the
11063 * corresponding parent triggers.
11065 GetForeignKeyCheckTriggers(trigrel
,
11066 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
11067 &insertTriggerOid
, &updateTriggerOid
);
11068 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
11069 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
11071 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
11072 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
11075 CommandCounterIncrement();
11080 * GetForeignKeyActionTriggers
11081 * Returns delete and update "action" triggers of the given relation
11082 * belonging to the given constraint
11085 GetForeignKeyActionTriggers(Relation trigrel
,
11086 Oid conoid
, Oid confrelid
, Oid conrelid
,
11087 Oid
*deleteTriggerOid
,
11088 Oid
*updateTriggerOid
)
11094 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
11096 Anum_pg_trigger_tgconstraint
,
11097 BTEqualStrategyNumber
, F_OIDEQ
,
11098 ObjectIdGetDatum(conoid
));
11100 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11102 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11104 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11106 if (trgform
->tgconstrrelid
!= conrelid
)
11108 if (trgform
->tgrelid
!= confrelid
)
11110 /* Only ever look at "action" triggers on the PK side. */
11111 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
11113 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
11115 Assert(*deleteTriggerOid
== InvalidOid
);
11116 *deleteTriggerOid
= trgform
->oid
;
11118 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11120 Assert(*updateTriggerOid
== InvalidOid
);
11121 *updateTriggerOid
= trgform
->oid
;
11123 #ifndef USE_ASSERT_CHECKING
11124 /* In an assert-enabled build, continue looking to find duplicates */
11125 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
11130 if (!OidIsValid(*deleteTriggerOid
))
11131 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
11133 if (!OidIsValid(*updateTriggerOid
))
11134 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
11137 systable_endscan(scan
);
11141 * GetForeignKeyCheckTriggers
11142 * Returns insert and update "check" triggers of the given relation
11143 * belonging to the given constraint
11146 GetForeignKeyCheckTriggers(Relation trigrel
,
11147 Oid conoid
, Oid confrelid
, Oid conrelid
,
11148 Oid
*insertTriggerOid
,
11149 Oid
*updateTriggerOid
)
11155 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
11157 Anum_pg_trigger_tgconstraint
,
11158 BTEqualStrategyNumber
, F_OIDEQ
,
11159 ObjectIdGetDatum(conoid
));
11161 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11163 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11165 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11167 if (trgform
->tgconstrrelid
!= confrelid
)
11169 if (trgform
->tgrelid
!= conrelid
)
11171 /* Only ever look at "check" triggers on the FK side. */
11172 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
11174 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
11176 Assert(*insertTriggerOid
== InvalidOid
);
11177 *insertTriggerOid
= trgform
->oid
;
11179 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11181 Assert(*updateTriggerOid
== InvalidOid
);
11182 *updateTriggerOid
= trgform
->oid
;
11184 #ifndef USE_ASSERT_CHECKING
11185 /* In an assert-enabled build, continue looking to find duplicates. */
11186 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
11191 if (!OidIsValid(*insertTriggerOid
))
11192 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
11194 if (!OidIsValid(*updateTriggerOid
))
11195 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
11198 systable_endscan(scan
);
11202 * ALTER TABLE ALTER CONSTRAINT
11204 * Update the attributes of a constraint.
11206 * Currently only works for Foreign Key constraints.
11208 * If the constraint is modified, returns its address; otherwise, return
11209 * InvalidObjectAddress.
11211 static ObjectAddress
11212 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
11213 bool recursing
, LOCKMODE lockmode
)
11215 Constraint
*cmdcon
;
11219 ScanKeyData skey
[3];
11220 HeapTuple contuple
;
11221 Form_pg_constraint currcon
;
11222 ObjectAddress address
;
11223 List
*otherrelids
= NIL
;
11226 cmdcon
= castNode(Constraint
, cmd
->def
);
11228 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11229 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11232 * Find and check the target constraint
11234 ScanKeyInit(&skey
[0],
11235 Anum_pg_constraint_conrelid
,
11236 BTEqualStrategyNumber
, F_OIDEQ
,
11237 ObjectIdGetDatum(RelationGetRelid(rel
)));
11238 ScanKeyInit(&skey
[1],
11239 Anum_pg_constraint_contypid
,
11240 BTEqualStrategyNumber
, F_OIDEQ
,
11241 ObjectIdGetDatum(InvalidOid
));
11242 ScanKeyInit(&skey
[2],
11243 Anum_pg_constraint_conname
,
11244 BTEqualStrategyNumber
, F_NAMEEQ
,
11245 CStringGetDatum(cmdcon
->conname
));
11246 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11247 true, NULL
, 3, skey
);
11249 /* There can be at most one matching row */
11250 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
11252 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11253 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11254 cmdcon
->conname
, RelationGetRelationName(rel
))));
11256 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11257 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
11259 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11260 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11261 cmdcon
->conname
, RelationGetRelationName(rel
))));
11264 * If it's not the topmost constraint, raise an error.
11266 * Altering a non-topmost constraint leaves some triggers untouched, since
11267 * they are not directly connected to this constraint; also, pg_dump would
11268 * ignore the deferrability status of the individual constraint, since it
11269 * only dumps topmost constraints. Avoid these problems by refusing this
11270 * operation and telling the user to alter the parent constraint instead.
11272 if (OidIsValid(currcon
->conparentid
))
11275 Oid parent
= currcon
->conparentid
;
11276 char *ancestorname
= NULL
;
11277 char *ancestortable
= NULL
;
11279 /* Loop to find the topmost constraint */
11280 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
11282 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
11284 /* If no parent, this is the constraint we want */
11285 if (!OidIsValid(contup
->conparentid
))
11287 ancestorname
= pstrdup(NameStr(contup
->conname
));
11288 ancestortable
= get_rel_name(contup
->conrelid
);
11289 ReleaseSysCache(tp
);
11293 parent
= contup
->conparentid
;
11294 ReleaseSysCache(tp
);
11298 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11299 cmdcon
->conname
, RelationGetRelationName(rel
)),
11300 ancestorname
&& ancestortable
?
11301 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11302 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
11303 errhint("You may alter the constraint it derives from instead.")));
11307 * Do the actual catalog work. We can skip changing if already in the
11308 * desired state, but not if a partitioned table: partitions need to be
11309 * processed regardless, in case they had the constraint locally changed.
11311 address
= InvalidObjectAddress
;
11312 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11313 currcon
->condeferred
!= cmdcon
->initdeferred
||
11314 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11316 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
11317 &otherrelids
, lockmode
))
11318 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
11322 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11323 * having the constraint itself; here we also invalidate for relations
11324 * that have any triggers that are part of the constraint.
11326 foreach(lc
, otherrelids
)
11327 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
11329 systable_endscan(scan
);
11331 table_close(tgrel
, RowExclusiveLock
);
11332 table_close(conrel
, RowExclusiveLock
);
11338 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11339 * constraint is altered.
11341 * *otherrelids is appended OIDs of relations containing affected triggers.
11343 * Note that we must recurse even when the values are correct, in case
11344 * indirect descendants have had their constraints altered locally.
11345 * (This could be avoided if we forbade altering constraints in partitions
11346 * but existing releases don't do that.)
11349 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
11350 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
11353 Form_pg_constraint currcon
;
11356 bool changed
= false;
11358 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11359 conoid
= currcon
->oid
;
11360 refrelid
= currcon
->confrelid
;
11363 * Update pg_constraint with the flags from cmdcon.
11365 * If called to modify a constraint that's already in the desired state,
11366 * silently do nothing.
11368 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11369 currcon
->condeferred
!= cmdcon
->initdeferred
)
11371 HeapTuple copyTuple
;
11372 Form_pg_constraint copy_con
;
11375 SysScanDesc tgscan
;
11377 copyTuple
= heap_copytuple(contuple
);
11378 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11379 copy_con
->condeferrable
= cmdcon
->deferrable
;
11380 copy_con
->condeferred
= cmdcon
->initdeferred
;
11381 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11383 InvokeObjectPostAlterHook(ConstraintRelationId
,
11386 heap_freetuple(copyTuple
);
11389 /* Make new constraint flags visible to others */
11390 CacheInvalidateRelcache(rel
);
11393 * Now we need to update the multiple entries in pg_trigger that
11394 * implement the constraint.
11396 ScanKeyInit(&tgkey
,
11397 Anum_pg_trigger_tgconstraint
,
11398 BTEqualStrategyNumber
, F_OIDEQ
,
11399 ObjectIdGetDatum(conoid
));
11400 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11402 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11404 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11405 Form_pg_trigger copy_tg
;
11406 HeapTuple tgCopyTuple
;
11409 * Remember OIDs of other relation(s) involved in FK constraint.
11410 * (Note: it's likely that we could skip forcing a relcache inval
11411 * for other rels that don't have a trigger whose properties
11412 * change, but let's be conservative.)
11414 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11415 *otherrelids
= list_append_unique_oid(*otherrelids
,
11419 * Update deferrability of RI_FKey_noaction_del,
11420 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11421 * triggers, but not others; see createForeignKeyActionTriggers
11422 * and CreateFKCheckTrigger.
11424 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11425 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11426 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11427 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11430 tgCopyTuple
= heap_copytuple(tgtuple
);
11431 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11433 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11434 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11435 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11437 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11439 heap_freetuple(tgCopyTuple
);
11442 systable_endscan(tgscan
);
11446 * If the table at either end of the constraint is partitioned, we need to
11447 * recurse and handle every constraint that is a child of this one.
11449 * (This assumes that the recurse flag is forcibly set for partitioned
11450 * tables, and not set for legacy inheritance, though we don't check for
11453 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11454 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11458 HeapTuple childtup
;
11461 Anum_pg_constraint_conparentid
,
11462 BTEqualStrategyNumber
, F_OIDEQ
,
11463 ObjectIdGetDatum(conoid
));
11465 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11466 true, NULL
, 1, &pkey
);
11468 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11470 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11473 childrel
= table_open(childcon
->conrelid
, lockmode
);
11474 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11475 otherrelids
, lockmode
);
11476 table_close(childrel
, NoLock
);
11479 systable_endscan(pscan
);
11486 * ALTER TABLE VALIDATE CONSTRAINT
11488 * XXX The reason we handle recursion here rather than at Phase 1 is because
11489 * there's no good way to skip recursing when handling foreign keys: there is
11490 * no need to lock children in that case, yet we wouldn't be able to avoid
11491 * doing so at that level.
11493 * Return value is the address of the validated constraint. If the constraint
11494 * was already validated, InvalidObjectAddress is returned.
11496 static ObjectAddress
11497 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
11498 bool recurse
, bool recursing
, LOCKMODE lockmode
)
11502 ScanKeyData skey
[3];
11504 Form_pg_constraint con
;
11505 ObjectAddress address
;
11507 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11510 * Find and check the target constraint
11512 ScanKeyInit(&skey
[0],
11513 Anum_pg_constraint_conrelid
,
11514 BTEqualStrategyNumber
, F_OIDEQ
,
11515 ObjectIdGetDatum(RelationGetRelid(rel
)));
11516 ScanKeyInit(&skey
[1],
11517 Anum_pg_constraint_contypid
,
11518 BTEqualStrategyNumber
, F_OIDEQ
,
11519 ObjectIdGetDatum(InvalidOid
));
11520 ScanKeyInit(&skey
[2],
11521 Anum_pg_constraint_conname
,
11522 BTEqualStrategyNumber
, F_NAMEEQ
,
11523 CStringGetDatum(constrName
));
11524 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11525 true, NULL
, 3, skey
);
11527 /* There can be at most one matching row */
11528 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
11530 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11531 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11532 constrName
, RelationGetRelationName(rel
))));
11534 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11535 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
11536 con
->contype
!= CONSTRAINT_CHECK
)
11538 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11539 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11540 constrName
, RelationGetRelationName(rel
))));
11542 if (!con
->convalidated
)
11544 AlteredTableInfo
*tab
;
11545 HeapTuple copyTuple
;
11546 Form_pg_constraint copy_con
;
11548 if (con
->contype
== CONSTRAINT_FOREIGN
)
11550 NewConstraint
*newcon
;
11551 Constraint
*fkconstraint
;
11553 /* Queue validation for phase 3 */
11554 fkconstraint
= makeNode(Constraint
);
11555 /* for now this is all we need */
11556 fkconstraint
->conname
= constrName
;
11558 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11559 newcon
->name
= constrName
;
11560 newcon
->contype
= CONSTR_FOREIGN
;
11561 newcon
->refrelid
= con
->confrelid
;
11562 newcon
->refindid
= con
->conindid
;
11563 newcon
->conid
= con
->oid
;
11564 newcon
->qual
= (Node
*) fkconstraint
;
11566 /* Find or create work queue entry for this table */
11567 tab
= ATGetQueueEntry(wqueue
, rel
);
11568 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11571 * We disallow creating invalid foreign keys to or from
11572 * partitioned tables, so ignoring the recursion bit is okay.
11575 else if (con
->contype
== CONSTRAINT_CHECK
)
11577 List
*children
= NIL
;
11579 NewConstraint
*newcon
;
11584 * If we're recursing, the parent has already done this, so skip
11585 * it. Also, if the constraint is a NO INHERIT constraint, we
11586 * shouldn't try to look for it in the children.
11588 if (!recursing
&& !con
->connoinherit
)
11589 children
= find_all_inheritors(RelationGetRelid(rel
),
11593 * For CHECK constraints, we must ensure that we only mark the
11594 * constraint as validated on the parent if it's already validated
11597 * We recurse before validating on the parent, to reduce risk of
11600 foreach(child
, children
)
11602 Oid childoid
= lfirst_oid(child
);
11605 if (childoid
== RelationGetRelid(rel
))
11609 * If we are told not to recurse, there had better not be any
11610 * child tables, because we can't mark the constraint on the
11611 * parent valid unless it is valid for all child tables.
11615 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
11616 errmsg("constraint must be validated on child tables too")));
11618 /* find_all_inheritors already got lock */
11619 childrel
= table_open(childoid
, NoLock
);
11621 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
11623 table_close(childrel
, NoLock
);
11626 /* Queue validation for phase 3 */
11627 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11628 newcon
->name
= constrName
;
11629 newcon
->contype
= CONSTR_CHECK
;
11630 newcon
->refrelid
= InvalidOid
;
11631 newcon
->refindid
= InvalidOid
;
11632 newcon
->conid
= con
->oid
;
11634 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
11635 Anum_pg_constraint_conbin
);
11636 conbin
= TextDatumGetCString(val
);
11637 newcon
->qual
= (Node
*) stringToNode(conbin
);
11639 /* Find or create work queue entry for this table */
11640 tab
= ATGetQueueEntry(wqueue
, rel
);
11641 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11644 * Invalidate relcache so that others see the new validated
11647 CacheInvalidateRelcache(rel
);
11651 * Now update the catalog, while we have the door open.
11653 copyTuple
= heap_copytuple(tuple
);
11654 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11655 copy_con
->convalidated
= true;
11656 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11658 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
11660 heap_freetuple(copyTuple
);
11662 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
11665 address
= InvalidObjectAddress
; /* already validated */
11667 systable_endscan(scan
);
11669 table_close(conrel
, RowExclusiveLock
);
11676 * transformColumnNameList - transform list of column names
11678 * Lookup each name and return its attnum and, optionally, type OID
11680 * Note: the name of this function suggests that it's general-purpose,
11681 * but actually it's only used to look up names appearing in foreign-key
11682 * clauses. The error messages would need work to use it in other cases,
11683 * and perhaps the validity checks as well.
11686 transformColumnNameList(Oid relId
, List
*colList
,
11687 int16
*attnums
, Oid
*atttypids
)
11693 foreach(l
, colList
)
11695 char *attname
= strVal(lfirst(l
));
11696 HeapTuple atttuple
;
11697 Form_pg_attribute attform
;
11699 atttuple
= SearchSysCacheAttName(relId
, attname
);
11700 if (!HeapTupleIsValid(atttuple
))
11702 (errcode(ERRCODE_UNDEFINED_COLUMN
),
11703 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11705 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
11706 if (attform
->attnum
< 0)
11708 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
11709 errmsg("system columns cannot be used in foreign keys")));
11710 if (attnum
>= INDEX_MAX_KEYS
)
11712 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
11713 errmsg("cannot have more than %d keys in a foreign key",
11715 attnums
[attnum
] = attform
->attnum
;
11716 if (atttypids
!= NULL
)
11717 atttypids
[attnum
] = attform
->atttypid
;
11718 ReleaseSysCache(atttuple
);
11726 * transformFkeyGetPrimaryKey -
11728 * Look up the names, attnums, and types of the primary key attributes
11729 * for the pkrel. Also return the index OID and index opclasses of the
11730 * index supporting the primary key.
11732 * All parameters except pkrel are output parameters. Also, the function
11733 * return value is the number of attributes in the primary key.
11735 * Used when the column list in the REFERENCES specification is omitted.
11738 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
11739 List
**attnamelist
,
11740 int16
*attnums
, Oid
*atttypids
,
11743 List
*indexoidlist
;
11744 ListCell
*indexoidscan
;
11745 HeapTuple indexTuple
= NULL
;
11746 Form_pg_index indexStruct
= NULL
;
11747 Datum indclassDatum
;
11748 oidvector
*indclass
;
11752 * Get the list of index OIDs for the table from the relcache, and look up
11753 * each one in the pg_index syscache until we find one marked primary key
11754 * (hopefully there isn't more than one such). Insist it's valid, too.
11756 *indexOid
= InvalidOid
;
11758 indexoidlist
= RelationGetIndexList(pkrel
);
11760 foreach(indexoidscan
, indexoidlist
)
11762 Oid indexoid
= lfirst_oid(indexoidscan
);
11764 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11765 if (!HeapTupleIsValid(indexTuple
))
11766 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11767 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11768 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
11771 * Refuse to use a deferrable primary key. This is per SQL spec,
11772 * and there would be a lot of interesting semantic problems if we
11773 * tried to allow it.
11775 if (!indexStruct
->indimmediate
)
11777 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11778 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11779 RelationGetRelationName(pkrel
))));
11781 *indexOid
= indexoid
;
11784 ReleaseSysCache(indexTuple
);
11787 list_free(indexoidlist
);
11790 * Check that we found it
11792 if (!OidIsValid(*indexOid
))
11794 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11795 errmsg("there is no primary key for referenced table \"%s\"",
11796 RelationGetRelationName(pkrel
))));
11798 /* Must get indclass the hard way */
11799 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11800 Anum_pg_index_indclass
);
11801 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11804 * Now build the list of PK attributes from the indkey definition (we
11805 * assume a primary key cannot have expressional elements)
11807 *attnamelist
= NIL
;
11808 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
11810 int pkattno
= indexStruct
->indkey
.values
[i
];
11812 attnums
[i
] = pkattno
;
11813 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
11814 opclasses
[i
] = indclass
->values
[i
];
11815 *attnamelist
= lappend(*attnamelist
,
11816 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
11819 ReleaseSysCache(indexTuple
);
11825 * transformFkeyCheckAttrs -
11827 * Make sure that the attributes of a referenced table belong to a unique
11828 * (or primary key) constraint. Return the OID of the index supporting
11829 * the constraint, as well as the opclasses associated with the index
11833 transformFkeyCheckAttrs(Relation pkrel
,
11834 int numattrs
, int16
*attnums
,
11835 Oid
*opclasses
) /* output parameter */
11837 Oid indexoid
= InvalidOid
;
11838 bool found
= false;
11839 bool found_deferrable
= false;
11840 List
*indexoidlist
;
11841 ListCell
*indexoidscan
;
11846 * Reject duplicate appearances of columns in the referenced-columns list.
11847 * Such a case is forbidden by the SQL standard, and even if we thought it
11848 * useful to allow it, there would be ambiguity about how to match the
11849 * list to unique indexes (in particular, it'd be unclear which index
11850 * opclass goes with which FK column).
11852 for (i
= 0; i
< numattrs
; i
++)
11854 for (j
= i
+ 1; j
< numattrs
; j
++)
11856 if (attnums
[i
] == attnums
[j
])
11858 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
11859 errmsg("foreign key referenced-columns list must not contain duplicates")));
11864 * Get the list of index OIDs for the table from the relcache, and look up
11865 * each one in the pg_index syscache, and match unique indexes to the list
11866 * of attnums we are given.
11868 indexoidlist
= RelationGetIndexList(pkrel
);
11870 foreach(indexoidscan
, indexoidlist
)
11872 HeapTuple indexTuple
;
11873 Form_pg_index indexStruct
;
11875 indexoid
= lfirst_oid(indexoidscan
);
11876 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11877 if (!HeapTupleIsValid(indexTuple
))
11878 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11879 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11882 * Must have the right number of columns; must be unique and not a
11883 * partial index; forget it if there are any expressions, too. Invalid
11884 * indexes are out as well.
11886 if (indexStruct
->indnkeyatts
== numattrs
&&
11887 indexStruct
->indisunique
&&
11888 indexStruct
->indisvalid
&&
11889 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
11890 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
11892 Datum indclassDatum
;
11893 oidvector
*indclass
;
11895 /* Must get indclass the hard way */
11896 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11897 Anum_pg_index_indclass
);
11898 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11901 * The given attnum list may match the index columns in any order.
11902 * Check for a match, and extract the appropriate opclasses while
11905 * We know that attnums[] is duplicate-free per the test at the
11906 * start of this function, and we checked above that the number of
11907 * index columns agrees, so if we find a match for each attnums[]
11908 * entry then we must have a one-to-one match in some order.
11910 for (i
= 0; i
< numattrs
; i
++)
11913 for (j
= 0; j
< numattrs
; j
++)
11915 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
11917 opclasses
[i
] = indclass
->values
[j
];
11927 * Refuse to use a deferrable unique/primary key. This is per SQL
11928 * spec, and there would be a lot of interesting semantic problems
11929 * if we tried to allow it.
11931 if (found
&& !indexStruct
->indimmediate
)
11934 * Remember that we found an otherwise matching index, so that
11935 * we can generate a more appropriate error message.
11937 found_deferrable
= true;
11941 ReleaseSysCache(indexTuple
);
11948 if (found_deferrable
)
11950 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11951 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
11952 RelationGetRelationName(pkrel
))));
11955 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
11956 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
11957 RelationGetRelationName(pkrel
))));
11960 list_free(indexoidlist
);
11968 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
11969 * Caller has equal regard for binary coercibility and for an exact match.
11971 static CoercionPathType
11972 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
11974 CoercionPathType ret
;
11976 if (targetTypeId
== sourceTypeId
)
11978 ret
= COERCION_PATH_RELABELTYPE
;
11979 *funcid
= InvalidOid
;
11983 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
11984 COERCION_IMPLICIT
, funcid
);
11985 if (ret
== COERCION_PATH_NONE
)
11986 /* A previously-relied-upon cast is now gone. */
11987 elog(ERROR
, "could not find cast from %u to %u",
11988 sourceTypeId
, targetTypeId
);
11995 * Permissions checks on the referenced table for ADD FOREIGN KEY
11997 * Note: we have already checked that the user owns the referencing table,
11998 * else we'd have failed much earlier; no additional checks are needed for it.
12001 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
12003 Oid roleid
= GetUserId();
12004 AclResult aclresult
;
12007 /* Okay if we have relation-level REFERENCES permission */
12008 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
12010 if (aclresult
== ACLCHECK_OK
)
12012 /* Else we must have REFERENCES on each column */
12013 for (i
= 0; i
< natts
; i
++)
12015 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
12016 roleid
, ACL_REFERENCES
);
12017 if (aclresult
!= ACLCHECK_OK
)
12018 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
12019 RelationGetRelationName(rel
));
12024 * Scan the existing rows in a table to verify they meet a proposed FK
12027 * Caller must have opened and locked both relations appropriately.
12030 validateForeignKeyConstraint(char *conname
,
12036 TupleTableSlot
*slot
;
12037 TableScanDesc scan
;
12038 Trigger trig
= {0};
12040 MemoryContext oldcxt
;
12041 MemoryContext perTupCxt
;
12044 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
12047 * Build a trigger call structure; we'll need it either way.
12049 trig
.tgoid
= InvalidOid
;
12050 trig
.tgname
= conname
;
12051 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
12052 trig
.tgisinternal
= true;
12053 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
12054 trig
.tgconstrindid
= pkindOid
;
12055 trig
.tgconstraint
= constraintOid
;
12056 trig
.tgdeferrable
= false;
12057 trig
.tginitdeferred
= false;
12058 /* we needn't fill in remaining fields */
12061 * See if we can do it with a single LEFT JOIN query. A false result
12062 * indicates we must proceed with the fire-the-trigger method.
12064 if (RI_Initial_Check(&trig
, rel
, pkrel
))
12068 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12069 * if that tuple had just been inserted. If any of those fail, it should
12070 * ereport(ERROR) and that's that.
12072 snapshot
= RegisterSnapshot(GetLatestSnapshot());
12073 slot
= table_slot_create(rel
, NULL
);
12074 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
12076 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
12077 "validateForeignKeyConstraint",
12078 ALLOCSET_SMALL_SIZES
);
12079 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
12081 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
12083 LOCAL_FCINFO(fcinfo
, 0);
12084 TriggerData trigdata
= {0};
12086 CHECK_FOR_INTERRUPTS();
12089 * Make a call to the trigger function
12091 * No parameters are passed, but we do set a context
12093 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
12096 * We assume RI_FKey_check_ins won't look at flinfo...
12098 trigdata
.type
= T_TriggerData
;
12099 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
12100 trigdata
.tg_relation
= rel
;
12101 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
12102 trigdata
.tg_trigslot
= slot
;
12103 trigdata
.tg_trigger
= &trig
;
12105 fcinfo
->context
= (Node
*) &trigdata
;
12107 RI_FKey_check_ins(fcinfo
);
12109 MemoryContextReset(perTupCxt
);
12112 MemoryContextSwitchTo(oldcxt
);
12113 MemoryContextDelete(perTupCxt
);
12114 table_endscan(scan
);
12115 UnregisterSnapshot(snapshot
);
12116 ExecDropSingleTupleTableSlot(slot
);
12120 * CreateFKCheckTrigger
12121 * Creates the insert (on_insert=true) or update "check" trigger that
12122 * implements a given foreign key
12124 * Returns the OID of the so created trigger.
12127 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
12128 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
12131 ObjectAddress trigAddress
;
12132 CreateTrigStmt
*fk_trigger
;
12135 * Note: for a self-referential FK (referencing and referenced tables are
12136 * the same), it is important that the ON UPDATE action fires before the
12137 * CHECK action, since both triggers will fire on the same row during an
12138 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12139 * state of the row. Triggers fire in name order, so we ensure this by
12140 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12141 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12143 fk_trigger
= makeNode(CreateTrigStmt
);
12144 fk_trigger
->replace
= false;
12145 fk_trigger
->isconstraint
= true;
12146 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
12147 fk_trigger
->relation
= NULL
;
12149 /* Either ON INSERT or ON UPDATE */
12152 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
12153 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
12157 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
12158 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12161 fk_trigger
->args
= NIL
;
12162 fk_trigger
->row
= true;
12163 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12164 fk_trigger
->columns
= NIL
;
12165 fk_trigger
->whenClause
= NULL
;
12166 fk_trigger
->transitionRels
= NIL
;
12167 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12168 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12169 fk_trigger
->constrrel
= NULL
;
12171 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
12172 constraintOid
, indexOid
, InvalidOid
,
12173 parentTrigOid
, NULL
, true, false);
12175 /* Make changes-so-far visible */
12176 CommandCounterIncrement();
12178 return trigAddress
.objectId
;
12182 * createForeignKeyActionTriggers
12183 * Create the referenced-side "action" triggers that implement a foreign
12186 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12190 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
12191 Oid constraintOid
, Oid indexOid
,
12192 Oid parentDelTrigger
, Oid parentUpdTrigger
,
12193 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
12195 CreateTrigStmt
*fk_trigger
;
12196 ObjectAddress trigAddress
;
12199 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12200 * DELETE action on the referenced table.
12202 fk_trigger
= makeNode(CreateTrigStmt
);
12203 fk_trigger
->replace
= false;
12204 fk_trigger
->isconstraint
= true;
12205 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12206 fk_trigger
->relation
= NULL
;
12207 fk_trigger
->args
= NIL
;
12208 fk_trigger
->row
= true;
12209 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12210 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
12211 fk_trigger
->columns
= NIL
;
12212 fk_trigger
->whenClause
= NULL
;
12213 fk_trigger
->transitionRels
= NIL
;
12214 fk_trigger
->constrrel
= NULL
;
12215 switch (fkconstraint
->fk_del_action
)
12217 case FKCONSTR_ACTION_NOACTION
:
12218 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12219 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12220 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
12222 case FKCONSTR_ACTION_RESTRICT
:
12223 fk_trigger
->deferrable
= false;
12224 fk_trigger
->initdeferred
= false;
12225 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
12227 case FKCONSTR_ACTION_CASCADE
:
12228 fk_trigger
->deferrable
= false;
12229 fk_trigger
->initdeferred
= false;
12230 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
12232 case FKCONSTR_ACTION_SETNULL
:
12233 fk_trigger
->deferrable
= false;
12234 fk_trigger
->initdeferred
= false;
12235 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
12237 case FKCONSTR_ACTION_SETDEFAULT
:
12238 fk_trigger
->deferrable
= false;
12239 fk_trigger
->initdeferred
= false;
12240 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
12243 elog(ERROR
, "unrecognized FK action type: %d",
12244 (int) fkconstraint
->fk_del_action
);
12248 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12249 RelationGetRelid(rel
),
12250 constraintOid
, indexOid
, InvalidOid
,
12251 parentDelTrigger
, NULL
, true, false);
12253 *deleteTrigOid
= trigAddress
.objectId
;
12255 /* Make changes-so-far visible */
12256 CommandCounterIncrement();
12259 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12260 * UPDATE action on the referenced table.
12262 fk_trigger
= makeNode(CreateTrigStmt
);
12263 fk_trigger
->replace
= false;
12264 fk_trigger
->isconstraint
= true;
12265 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12266 fk_trigger
->relation
= NULL
;
12267 fk_trigger
->args
= NIL
;
12268 fk_trigger
->row
= true;
12269 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12270 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12271 fk_trigger
->columns
= NIL
;
12272 fk_trigger
->whenClause
= NULL
;
12273 fk_trigger
->transitionRels
= NIL
;
12274 fk_trigger
->constrrel
= NULL
;
12275 switch (fkconstraint
->fk_upd_action
)
12277 case FKCONSTR_ACTION_NOACTION
:
12278 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12279 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12280 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
12282 case FKCONSTR_ACTION_RESTRICT
:
12283 fk_trigger
->deferrable
= false;
12284 fk_trigger
->initdeferred
= false;
12285 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
12287 case FKCONSTR_ACTION_CASCADE
:
12288 fk_trigger
->deferrable
= false;
12289 fk_trigger
->initdeferred
= false;
12290 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
12292 case FKCONSTR_ACTION_SETNULL
:
12293 fk_trigger
->deferrable
= false;
12294 fk_trigger
->initdeferred
= false;
12295 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
12297 case FKCONSTR_ACTION_SETDEFAULT
:
12298 fk_trigger
->deferrable
= false;
12299 fk_trigger
->initdeferred
= false;
12300 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
12303 elog(ERROR
, "unrecognized FK action type: %d",
12304 (int) fkconstraint
->fk_upd_action
);
12308 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12309 RelationGetRelid(rel
),
12310 constraintOid
, indexOid
, InvalidOid
,
12311 parentUpdTrigger
, NULL
, true, false);
12313 *updateTrigOid
= trigAddress
.objectId
;
12317 * createForeignKeyCheckTriggers
12318 * Create the referencing-side "check" triggers that implement a foreign
12321 * Returns the OIDs of the so created triggers in *insertTrigOid and
12325 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
12326 Constraint
*fkconstraint
, Oid constraintOid
,
12328 Oid parentInsTrigger
, Oid parentUpdTrigger
,
12329 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
12331 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12332 constraintOid
, indexOid
,
12333 parentInsTrigger
, true);
12334 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12335 constraintOid
, indexOid
,
12336 parentUpdTrigger
, false);
12340 * ALTER TABLE DROP CONSTRAINT
12342 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12345 ATExecDropConstraint(Relation rel
, const char *constrName
,
12346 DropBehavior behavior
, bool recurse
,
12347 bool missing_ok
, LOCKMODE lockmode
)
12351 ScanKeyData skey
[3];
12353 bool found
= false;
12355 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12358 * Find and drop the target constraint
12360 ScanKeyInit(&skey
[0],
12361 Anum_pg_constraint_conrelid
,
12362 BTEqualStrategyNumber
, F_OIDEQ
,
12363 ObjectIdGetDatum(RelationGetRelid(rel
)));
12364 ScanKeyInit(&skey
[1],
12365 Anum_pg_constraint_contypid
,
12366 BTEqualStrategyNumber
, F_OIDEQ
,
12367 ObjectIdGetDatum(InvalidOid
));
12368 ScanKeyInit(&skey
[2],
12369 Anum_pg_constraint_conname
,
12370 BTEqualStrategyNumber
, F_NAMEEQ
,
12371 CStringGetDatum(constrName
));
12372 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12373 true, NULL
, 3, skey
);
12375 /* There can be at most one matching row */
12376 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12378 List
*readyRels
= NIL
;
12380 dropconstraint_internal(rel
, tuple
, behavior
, recurse
, false,
12381 missing_ok
, &readyRels
, lockmode
);
12385 systable_endscan(scan
);
12391 errcode(ERRCODE_UNDEFINED_OBJECT
),
12392 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12393 constrName
, RelationGetRelationName(rel
)));
12396 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12397 constrName
, RelationGetRelationName(rel
)));
12400 table_close(conrel
, RowExclusiveLock
);
12404 * Remove a constraint, using its pg_constraint tuple
12406 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12409 * Returns the address of the constraint being removed.
12411 static ObjectAddress
12412 dropconstraint_internal(Relation rel
, HeapTuple constraintTup
, DropBehavior behavior
,
12413 bool recurse
, bool recursing
, bool missing_ok
, List
**readyRels
,
12417 Form_pg_constraint con
;
12418 ObjectAddress conobj
;
12421 bool is_no_inherit_constraint
= false;
12422 bool dropping_pk
= false;
12424 List
*unconstrained_cols
= NIL
;
12427 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
12428 return InvalidObjectAddress
;
12429 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
12431 /* At top level, permission check was done in ATPrepCmd, else do it */
12433 ATSimplePermissions(AT_DropConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
12435 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12437 con
= (Form_pg_constraint
) GETSTRUCT(constraintTup
);
12438 constrName
= NameStr(con
->conname
);
12440 /* Don't allow drop of inherited constraints */
12441 if (con
->coninhcount
> 0 && !recursing
)
12443 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12444 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12445 constrName
, RelationGetRelationName(rel
))));
12448 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
12449 * more checks and actions below, so obtain the list of columns that are
12450 * constrained by the constraint being dropped.
12452 if (con
->contype
== CONSTRAINT_NOTNULL
)
12454 AttrNumber colnum
= extractNotNullColumn(constraintTup
);
12456 if (colnum
!= InvalidAttrNumber
)
12457 unconstrained_cols
= list_make1_int(colnum
);
12459 else if (con
->contype
== CONSTRAINT_PRIMARY
)
12467 dropping_pk
= true;
12469 adatum
= heap_getattr(constraintTup
, Anum_pg_constraint_conkey
,
12470 RelationGetDescr(conrel
), &isNull
);
12472 elog(ERROR
, "null conkey for constraint %u", con
->oid
);
12473 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
12474 numkeys
= ARR_DIMS(arr
)[0];
12475 if (ARR_NDIM(arr
) != 1 ||
12477 ARR_HASNULL(arr
) ||
12478 ARR_ELEMTYPE(arr
) != INT2OID
)
12479 elog(ERROR
, "conkey is not a 1-D smallint array");
12480 attnums
= (int16
*) ARR_DATA_PTR(arr
);
12482 for (int i
= 0; i
< numkeys
; i
++)
12483 unconstrained_cols
= lappend_int(unconstrained_cols
, attnums
[i
]);
12486 is_no_inherit_constraint
= con
->connoinherit
;
12489 * If it's a foreign-key constraint, we'd better lock the referenced table
12490 * and check that that's not in use, just as we've already done for the
12491 * constrained table (else we might, eg, be dropping a trigger that has
12492 * unfired events). But we can/must skip that in the self-referential
12495 if (con
->contype
== CONSTRAINT_FOREIGN
&&
12496 con
->confrelid
!= RelationGetRelid(rel
))
12500 /* Must match lock taken by RemoveTriggerById: */
12501 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
12502 CheckTableNotInUse(frel
, "ALTER TABLE");
12503 table_close(frel
, NoLock
);
12507 * Perform the actual constraint deletion
12509 ObjectAddressSet(conobj
, ConstraintRelationId
, con
->oid
);
12510 performDeletion(&conobj
, behavior
, 0);
12513 * If this was a NOT NULL or the primary key, the constrained columns must
12514 * have had pg_attribute.attnotnull set. See if we need to reset it, and
12517 if (unconstrained_cols
)
12524 /* Make the above deletion visible */
12525 CommandCounterIncrement();
12527 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
12530 * We want to test columns for their presence in the primary key, but
12531 * only if we're not dropping it.
12533 pkcols
= dropping_pk
? NULL
:
12534 RelationGetIndexAttrBitmap(rel
,
12535 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
12536 ircols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_IDENTITY_KEY
);
12538 foreach(lc
, unconstrained_cols
)
12540 AttrNumber attnum
= lfirst_int(lc
);
12543 Form_pg_attribute attForm
;
12546 * Obtain pg_attribute tuple and verify conditions on it. We use
12547 * a copy we can scribble on.
12549 atttup
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
12550 if (!HeapTupleIsValid(atttup
))
12551 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
12552 attnum
, RelationGetRelid(rel
));
12553 attForm
= (Form_pg_attribute
) GETSTRUCT(atttup
);
12556 * Since the above deletion has been made visible, we can now
12557 * search for any remaining constraints on this column (or these
12558 * columns, in the case we're dropping a multicol primary key.)
12559 * Then, verify whether any further NOT NULL or primary key
12560 * exists, and reset attnotnull if none.
12562 * However, if this is a generated identity column, abort the
12563 * whole thing with a specific error message, because the
12564 * constraint is required in that case.
12566 contup
= findNotNullConstraintAttnum(RelationGetRelid(rel
), attnum
);
12568 bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
12573 * It's not valid to drop the not-null constraint for a GENERATED
12574 * AS IDENTITY column.
12576 if (attForm
->attidentity
)
12578 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12579 errmsg("column \"%s\" of relation \"%s\" is an identity column",
12580 get_attname(RelationGetRelid(rel
), attnum
,
12582 RelationGetRelationName(rel
)));
12585 * It's not valid to drop the not-null constraint for a column in
12586 * the replica identity index, either. (FULL is not affected.)
12588 if (bms_is_member(lfirst_int(lc
) - FirstLowInvalidHeapAttributeNumber
, ircols
))
12590 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12591 errmsg("column \"%s\" is in index used as replica identity",
12592 get_attname(RelationGetRelid(rel
), lfirst_int(lc
), false)));
12594 /* Reset attnotnull */
12595 if (attForm
->attnotnull
)
12597 attForm
->attnotnull
= false;
12598 CatalogTupleUpdate(attrel
, &atttup
->t_self
, atttup
);
12601 table_close(attrel
, RowExclusiveLock
);
12605 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
12606 * are dropped via the dependency mechanism, so we're done here.
12608 if (con
->contype
!= CONSTRAINT_CHECK
&&
12609 con
->contype
!= CONSTRAINT_NOTNULL
&&
12610 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
12612 table_close(conrel
, RowExclusiveLock
);
12617 * Propagate to children as appropriate. Unlike most other ALTER
12618 * routines, we have to do this one level of recursion at a time; we can't
12619 * use find_all_inheritors to do it in one pass.
12621 if (!is_no_inherit_constraint
)
12622 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
12627 * For a partitioned table, if partitions exist and we are told not to
12628 * recurse, it's a user error. It doesn't make sense to have a constraint
12629 * be defined only on the parent, especially if it's a partitioned table.
12631 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
12632 children
!= NIL
&& !recurse
)
12634 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12635 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12636 errhint("Do not specify the ONLY keyword.")));
12638 /* For not-null constraints we recurse by column name */
12639 if (con
->contype
== CONSTRAINT_NOTNULL
)
12640 colname
= NameStr(TupleDescAttr(RelationGetDescr(rel
),
12641 linitial_int(unconstrained_cols
) - 1)->attname
);
12643 colname
= NULL
; /* keep compiler quiet */
12645 foreach(child
, children
)
12647 Oid childrelid
= lfirst_oid(child
);
12650 Form_pg_constraint childcon
;
12652 if (list_member_oid(*readyRels
, childrelid
))
12653 continue; /* child already processed */
12655 /* find_inheritance_children already got lock */
12656 childrel
= table_open(childrelid
, NoLock
);
12657 CheckTableNotInUse(childrel
, "ALTER TABLE");
12660 * We search for not-null constraint by column number, and other
12661 * constraints by name.
12663 if (con
->contype
== CONSTRAINT_NOTNULL
)
12665 tuple
= findNotNullConstraint(childrelid
, colname
);
12666 if (!HeapTupleIsValid(tuple
))
12667 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12668 colname
, RelationGetRelid(childrel
));
12673 ScanKeyData skey
[3];
12675 ScanKeyInit(&skey
[0],
12676 Anum_pg_constraint_conrelid
,
12677 BTEqualStrategyNumber
, F_OIDEQ
,
12678 ObjectIdGetDatum(childrelid
));
12679 ScanKeyInit(&skey
[1],
12680 Anum_pg_constraint_contypid
,
12681 BTEqualStrategyNumber
, F_OIDEQ
,
12682 ObjectIdGetDatum(InvalidOid
));
12683 ScanKeyInit(&skey
[2],
12684 Anum_pg_constraint_conname
,
12685 BTEqualStrategyNumber
, F_NAMEEQ
,
12686 CStringGetDatum(constrName
));
12687 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12688 true, NULL
, 3, skey
);
12689 /* There can only be one, so no need to loop */
12690 tuple
= systable_getnext(scan
);
12691 if (!HeapTupleIsValid(tuple
))
12693 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12694 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12696 RelationGetRelationName(childrel
))));
12697 tuple
= heap_copytuple(tuple
);
12698 systable_endscan(scan
);
12701 childcon
= (Form_pg_constraint
) GETSTRUCT(tuple
);
12703 /* Right now only CHECK and not-null constraints can be inherited */
12704 if (childcon
->contype
!= CONSTRAINT_CHECK
&&
12705 childcon
->contype
!= CONSTRAINT_NOTNULL
)
12706 elog(ERROR
, "inherited constraint is not a CHECK or not-null constraint");
12708 if (childcon
->coninhcount
<= 0) /* shouldn't happen */
12709 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
12710 childrelid
, NameStr(childcon
->conname
));
12715 * If the child constraint has other definition sources, just
12716 * decrement its inheritance count; if not, recurse to delete it.
12718 if (childcon
->coninhcount
== 1 && !childcon
->conislocal
)
12720 /* Time to delete this child constraint, too */
12721 dropconstraint_internal(childrel
, tuple
, behavior
,
12722 recurse
, true, missing_ok
, readyRels
,
12727 /* Child constraint must survive my deletion */
12728 childcon
->coninhcount
--;
12729 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
12731 /* Make update visible */
12732 CommandCounterIncrement();
12738 * If we were told to drop ONLY in this table (no recursion) and
12739 * there are no further parents for this constraint, we need to
12740 * mark the inheritors' constraints as locally defined rather than
12743 childcon
->coninhcount
--;
12744 if (childcon
->coninhcount
== 0)
12745 childcon
->conislocal
= true;
12747 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
12749 /* Make update visible */
12750 CommandCounterIncrement();
12753 heap_freetuple(tuple
);
12755 table_close(childrel
, NoLock
);
12759 * In addition, when dropping a primary key from a legacy-inheritance
12760 * parent table, we must recurse to children to mark the corresponding NOT
12761 * NULL constraint as no longer inherited, or drop it if this its last
12764 if (con
->contype
== CONSTRAINT_PRIMARY
&&
12765 rel
->rd_rel
->relkind
== RELKIND_RELATION
&&
12766 rel
->rd_rel
->relhassubclass
)
12768 List
*colnames
= NIL
;
12770 List
*pkready
= NIL
;
12773 * Because primary keys are always marked as NO INHERIT, we don't have
12774 * a list of children yet, so obtain one now.
12776 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
12779 * Find out the list of column names to process. Fortunately, we
12780 * already have the list of column numbers.
12782 foreach(lc
, unconstrained_cols
)
12784 colnames
= lappend(colnames
, get_attname(RelationGetRelid(rel
),
12785 lfirst_int(lc
), false));
12788 foreach(child
, children
)
12790 Oid childrelid
= lfirst_oid(child
);
12793 if (list_member_oid(pkready
, childrelid
))
12794 continue; /* child already processed */
12796 /* find_inheritance_children already got lock */
12797 childrel
= table_open(childrelid
, NoLock
);
12798 CheckTableNotInUse(childrel
, "ALTER TABLE");
12800 foreach(lc
, colnames
)
12803 char *colName
= lfirst(lc
);
12805 contup
= findNotNullConstraint(childrelid
, colName
);
12806 if (contup
== NULL
)
12807 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
12808 colName
, RelationGetRelationName(childrel
));
12810 dropconstraint_internal(childrel
, contup
,
12811 DROP_RESTRICT
, true, true,
12817 table_close(childrel
, NoLock
);
12819 pkready
= lappend_oid(pkready
, childrelid
);
12823 table_close(conrel
, RowExclusiveLock
);
12829 * ALTER COLUMN TYPE
12831 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12832 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12833 * transformed (and must be, because we rely on some transformed fields).
12835 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12836 * table will be done "in parallel" during phase 3, so all the USING
12837 * expressions should be parsed assuming the original column types. Also,
12838 * this allows a USING expression to refer to a field that will be dropped.
12840 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12841 * the first two execution steps in phase 2; they must not see the effects
12842 * of any other subcommand types, since the USING expressions are parsed
12843 * against the unmodified table's state.
12846 ATPrepAlterColumnType(List
**wqueue
,
12847 AlteredTableInfo
*tab
, Relation rel
,
12848 bool recurse
, bool recursing
,
12849 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
12850 AlterTableUtilityContext
*context
)
12852 char *colName
= cmd
->name
;
12853 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
12854 TypeName
*typeName
= def
->typeName
;
12855 Node
*transform
= def
->cooked_default
;
12857 Form_pg_attribute attTup
;
12860 int32 targettypmod
;
12862 NewColumnValue
*newval
;
12863 ParseState
*pstate
= make_parsestate(NULL
);
12864 AclResult aclresult
;
12867 if (rel
->rd_rel
->reloftype
&& !recursing
)
12869 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12870 errmsg("cannot alter column type of typed table")));
12872 /* lookup the attribute so we can check inheritance status */
12873 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
12874 if (!HeapTupleIsValid(tuple
))
12876 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12877 errmsg("column \"%s\" of relation \"%s\" does not exist",
12878 colName
, RelationGetRelationName(rel
))));
12879 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
12880 attnum
= attTup
->attnum
;
12882 /* Can't alter a system attribute */
12885 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12886 errmsg("cannot alter system column \"%s\"",
12890 * Don't alter inherited columns. At outer level, there had better not be
12891 * any inherited definition; when recursing, we assume this was checked at
12892 * the parent level (see below).
12894 if (attTup
->attinhcount
> 0 && !recursing
)
12896 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12897 errmsg("cannot alter inherited column \"%s\"",
12900 /* Don't alter columns used in the partition key */
12901 if (has_partition_attrs(rel
,
12902 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
12905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12906 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12907 colName
, RelationGetRelationName(rel
))));
12909 /* Look up the target type */
12910 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
12912 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
12913 if (aclresult
!= ACLCHECK_OK
)
12914 aclcheck_error_type(aclresult
, targettype
);
12916 /* And the collation */
12917 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
12919 /* make sure datatype is legal for a column */
12920 CheckAttributeType(colName
, targettype
, targetcollid
,
12921 list_make1_oid(rel
->rd_rel
->reltype
),
12924 if (tab
->relkind
== RELKIND_RELATION
||
12925 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
12928 * Set up an expression to transform the old data value to the new
12929 * type. If a USING option was given, use the expression as
12930 * transformed by transformAlterTableStmt, else just take the old
12931 * value and try to coerce it. We do this first so that type
12932 * incompatibility can be detected before we waste effort, and because
12933 * we need the expression to be parsed against the original table row
12938 transform
= (Node
*) makeVar(1, attnum
,
12939 attTup
->atttypid
, attTup
->atttypmod
,
12940 attTup
->attcollation
,
12944 transform
= coerce_to_target_type(pstate
,
12945 transform
, exprType(transform
),
12946 targettype
, targettypmod
,
12947 COERCION_ASSIGNMENT
,
12948 COERCE_IMPLICIT_CAST
,
12950 if (transform
== NULL
)
12952 /* error text depends on whether USING was specified or not */
12953 if (def
->cooked_default
!= NULL
)
12955 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12956 errmsg("result of USING clause for column \"%s\""
12957 " cannot be cast automatically to type %s",
12958 colName
, format_type_be(targettype
)),
12959 errhint("You might need to add an explicit cast.")));
12962 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12963 errmsg("column \"%s\" cannot be cast automatically to type %s",
12964 colName
, format_type_be(targettype
)),
12965 /* translator: USING is SQL, don't translate it */
12966 errhint("You might need to specify \"USING %s::%s\".",
12967 quote_identifier(colName
),
12968 format_type_with_typemod(targettype
,
12972 /* Fix collations after all else */
12973 assign_expr_collations(pstate
, transform
);
12975 /* Plan the expr now so we can accurately assess the need to rewrite. */
12976 transform
= (Node
*) expression_planner((Expr
*) transform
);
12979 * Add a work queue item to make ATRewriteTable update the column
12982 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
12983 newval
->attnum
= attnum
;
12984 newval
->expr
= (Expr
*) transform
;
12985 newval
->is_generated
= false;
12987 tab
->newvals
= lappend(tab
->newvals
, newval
);
12988 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
12989 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
12991 else if (transform
)
12993 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12994 errmsg("\"%s\" is not a table",
12995 RelationGetRelationName(rel
))));
12997 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
13000 * For relations without storage, do this check now. Regular tables
13001 * will check it later when the table is being rewritten.
13003 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
13006 ReleaseSysCache(tuple
);
13009 * Recurse manually by queueing a new command for each child, if
13010 * necessary. We cannot apply ATSimpleRecursion here because we need to
13011 * remap attribute numbers in the USING expression, if any.
13013 * If we are told not to recurse, there had better not be any child
13014 * tables; else the alter would put them out of step.
13018 Oid relid
= RelationGetRelid(rel
);
13024 child_oids
= find_all_inheritors(relid
, lockmode
,
13025 &child_numparents
);
13028 * find_all_inheritors does the recursive search of the inheritance
13029 * hierarchy, so all we have to do is process all of the relids in the
13030 * list that it returns.
13032 forboth(lo
, child_oids
, li
, child_numparents
)
13034 Oid childrelid
= lfirst_oid(lo
);
13035 int numparents
= lfirst_int(li
);
13037 HeapTuple childtuple
;
13038 Form_pg_attribute childattTup
;
13040 if (childrelid
== relid
)
13043 /* find_all_inheritors already got lock */
13044 childrel
= relation_open(childrelid
, NoLock
);
13045 CheckTableNotInUse(childrel
, "ALTER TABLE");
13048 * Verify that the child doesn't have any inherited definitions of
13049 * this column that came from outside this inheritance hierarchy.
13050 * (renameatt makes a similar test, though in a different way
13051 * because of its different recursion mechanism.)
13053 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
13055 if (!HeapTupleIsValid(childtuple
))
13057 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13058 errmsg("column \"%s\" of relation \"%s\" does not exist",
13059 colName
, RelationGetRelationName(childrel
))));
13060 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
13062 if (childattTup
->attinhcount
> numparents
)
13064 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13065 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13066 colName
, RelationGetRelationName(childrel
))));
13068 ReleaseSysCache(childtuple
);
13071 * Remap the attribute numbers. If no USING expression was
13072 * specified, there is no need for this step.
13074 if (def
->cooked_default
)
13077 bool found_whole_row
;
13079 /* create a copy to scribble on */
13080 cmd
= copyObject(cmd
);
13082 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
13083 RelationGetDescr(rel
),
13085 ((ColumnDef
*) cmd
->def
)->cooked_default
=
13086 map_variable_attnos(def
->cooked_default
,
13089 InvalidOid
, &found_whole_row
);
13090 if (found_whole_row
)
13092 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13093 errmsg("cannot convert whole-row table reference"),
13094 errdetail("USING expression contains a whole-row table reference.")));
13097 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
13098 relation_close(childrel
, NoLock
);
13101 else if (!recursing
&&
13102 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
13104 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13105 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13108 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
13109 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
13113 * When the data type of a column is changed, a rewrite might not be required
13114 * if the new type is sufficiently identical to the old one, and the USING
13115 * clause isn't trying to insert some other value. It's safe to skip the
13116 * rewrite in these cases:
13118 * - the old type is binary coercible to the new type
13119 * - the new type is an unconstrained domain over the old type
13120 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13122 * In the case of a constrained domain, we could get by with scanning the
13123 * table and checking the constraint rather than actually rewriting it, but we
13124 * don't currently try to do that.
13127 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
13129 Assert(expr
!= NULL
);
13133 /* only one varno, so no need to check that */
13134 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
13136 else if (IsA(expr
, RelabelType
))
13137 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
13138 else if (IsA(expr
, CoerceToDomain
))
13140 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
13142 if (DomainHasConstraints(d
->resulttype
))
13144 expr
= (Node
*) d
->arg
;
13146 else if (IsA(expr
, FuncExpr
))
13148 FuncExpr
*f
= (FuncExpr
*) expr
;
13152 case F_TIMESTAMPTZ_TIMESTAMP
:
13153 case F_TIMESTAMP_TIMESTAMPTZ
:
13154 if (TimestampTimestampTzRequiresRewrite())
13157 expr
= linitial(f
->args
);
13169 * ALTER COLUMN .. SET DATA TYPE
13171 * Return the address of the modified column.
13173 static ObjectAddress
13174 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
13175 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
13177 char *colName
= cmd
->name
;
13178 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13179 TypeName
*typeName
= def
->typeName
;
13181 Form_pg_attribute attTup
,
13184 HeapTuple typeTuple
;
13185 Form_pg_type tform
;
13187 int32 targettypmod
;
13190 Relation attrelation
;
13192 ScanKeyData key
[3];
13195 ObjectAddress address
;
13198 * Clear all the missing values if we're rewriting the table, since this
13199 * renders them pointless.
13205 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
13206 RelationClearMissing(newrel
);
13207 relation_close(newrel
, NoLock
);
13208 /* make sure we don't conflict with later attribute modifications */
13209 CommandCounterIncrement();
13212 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
13214 /* Look up the target column */
13215 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
13216 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
13218 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13219 errmsg("column \"%s\" of relation \"%s\" does not exist",
13220 colName
, RelationGetRelationName(rel
))));
13221 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13222 attnum
= attTup
->attnum
;
13223 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
13225 /* Check for multiple ALTER TYPE on same column --- can't cope */
13226 if (attTup
->atttypid
!= attOldTup
->atttypid
||
13227 attTup
->atttypmod
!= attOldTup
->atttypmod
)
13229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13230 errmsg("cannot alter type of column \"%s\" twice",
13233 /* Look up the target type (should not fail, since prep found it) */
13234 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
13235 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
13236 targettype
= tform
->oid
;
13237 /* And the collation */
13238 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13241 * If there is a default expression for the column, get it and ensure we
13242 * can coerce it to the new datatype. (We must do this before changing
13243 * the column type, because build_column_default itself will try to
13244 * coerce, and will not issue the error message we want if it fails.)
13246 * We remove any implicit coercion steps at the top level of the old
13247 * default expression; this has been agreed to satisfy the principle of
13248 * least surprise. (The conversion to the new column type should act like
13249 * it started from what the user sees as the stored expression, and the
13250 * implicit coercions aren't going to be shown.)
13252 if (attTup
->atthasdef
)
13254 defaultexpr
= build_column_default(rel
, attnum
);
13255 Assert(defaultexpr
);
13256 defaultexpr
= strip_implicit_coercions(defaultexpr
);
13257 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
13258 defaultexpr
, exprType(defaultexpr
),
13259 targettype
, targettypmod
,
13260 COERCION_ASSIGNMENT
,
13261 COERCE_IMPLICIT_CAST
,
13263 if (defaultexpr
== NULL
)
13265 if (attTup
->attgenerated
)
13267 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13268 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13269 colName
, format_type_be(targettype
))));
13272 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13273 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13274 colName
, format_type_be(targettype
))));
13278 defaultexpr
= NULL
;
13281 * Find everything that depends on the column (constraints, indexes, etc),
13282 * and record enough information to let us recreate the objects.
13284 * The actual recreation does not happen here, but only after we have
13285 * performed all the individual ALTER TYPE operations. We have to save
13286 * the info before executing ALTER TYPE, though, else the deparser will
13289 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13291 ScanKeyInit(&key
[0],
13292 Anum_pg_depend_refclassid
,
13293 BTEqualStrategyNumber
, F_OIDEQ
,
13294 ObjectIdGetDatum(RelationRelationId
));
13295 ScanKeyInit(&key
[1],
13296 Anum_pg_depend_refobjid
,
13297 BTEqualStrategyNumber
, F_OIDEQ
,
13298 ObjectIdGetDatum(RelationGetRelid(rel
)));
13299 ScanKeyInit(&key
[2],
13300 Anum_pg_depend_refobjsubid
,
13301 BTEqualStrategyNumber
, F_INT4EQ
,
13302 Int32GetDatum((int32
) attnum
));
13304 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
13307 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13309 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13310 ObjectAddress foundObject
;
13312 foundObject
.classId
= foundDep
->classid
;
13313 foundObject
.objectId
= foundDep
->objid
;
13314 foundObject
.objectSubId
= foundDep
->objsubid
;
13316 switch (getObjectClass(&foundObject
))
13320 char relKind
= get_rel_relkind(foundObject
.objectId
);
13322 if (relKind
== RELKIND_INDEX
||
13323 relKind
== RELKIND_PARTITIONED_INDEX
)
13325 Assert(foundObject
.objectSubId
== 0);
13326 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
13328 else if (relKind
== RELKIND_SEQUENCE
)
13331 * This must be a SERIAL column's sequence. We need
13332 * not do anything to it.
13334 Assert(foundObject
.objectSubId
== 0);
13338 /* Not expecting any other direct dependencies... */
13339 elog(ERROR
, "unexpected object depending on column: %s",
13340 getObjectDescription(&foundObject
, false));
13345 case OCLASS_CONSTRAINT
:
13346 Assert(foundObject
.objectSubId
== 0);
13347 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
13350 case OCLASS_REWRITE
:
13351 /* XXX someday see if we can cope with revising views */
13353 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13354 errmsg("cannot alter type of a column used by a view or rule"),
13355 errdetail("%s depends on column \"%s\"",
13356 getObjectDescription(&foundObject
, false),
13360 case OCLASS_TRIGGER
:
13363 * A trigger can depend on a column because the column is
13364 * specified as an update target, or because the column is
13365 * used in the trigger's WHEN condition. The first case would
13366 * not require any extra work, but the second case would
13367 * require updating the WHEN expression, which will take a
13368 * significant amount of new code. Since we can't easily tell
13369 * which case applies, we punt for both. FIXME someday.
13372 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13373 errmsg("cannot alter type of a column used in a trigger definition"),
13374 errdetail("%s depends on column \"%s\"",
13375 getObjectDescription(&foundObject
, false),
13379 case OCLASS_POLICY
:
13382 * A policy can depend on a column because the column is
13383 * specified in the policy's USING or WITH CHECK qual
13384 * expressions. It might be possible to rewrite and recheck
13385 * the policy expression, but punt for now. It's certainly
13386 * easy enough to remove and recreate the policy; still, FIXME
13390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13391 errmsg("cannot alter type of a column used in a policy definition"),
13392 errdetail("%s depends on column \"%s\"",
13393 getObjectDescription(&foundObject
, false),
13397 case OCLASS_DEFAULT
:
13399 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
13401 if (col
.objectId
== RelationGetRelid(rel
) &&
13402 col
.objectSubId
== attnum
)
13405 * Ignore the column's own default expression, which
13406 * we will deal with below.
13408 Assert(defaultexpr
);
13413 * This must be a reference from the expression of a
13414 * generated column elsewhere in the same table.
13415 * Changing the type of a column that is used by a
13416 * generated column is not allowed by SQL standard, so
13417 * just punt for now. It might be doable with some
13418 * thinking and effort.
13421 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13422 errmsg("cannot alter type of a column used by a generated column"),
13423 errdetail("Column \"%s\" is used by generated column \"%s\".",
13425 get_attname(col
.objectId
,
13432 case OCLASS_STATISTIC_EXT
:
13435 * Give the extended-stats machinery a chance to fix anything
13436 * that this column type change would break.
13438 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
13444 case OCLASS_COLLATION
:
13445 case OCLASS_CONVERSION
:
13446 case OCLASS_LANGUAGE
:
13447 case OCLASS_LARGEOBJECT
:
13448 case OCLASS_OPERATOR
:
13449 case OCLASS_OPCLASS
:
13450 case OCLASS_OPFAMILY
:
13453 case OCLASS_AMPROC
:
13454 case OCLASS_SCHEMA
:
13455 case OCLASS_TSPARSER
:
13456 case OCLASS_TSDICT
:
13457 case OCLASS_TSTEMPLATE
:
13458 case OCLASS_TSCONFIG
:
13460 case OCLASS_ROLE_MEMBERSHIP
:
13461 case OCLASS_DATABASE
:
13462 case OCLASS_TBLSPACE
:
13464 case OCLASS_FOREIGN_SERVER
:
13465 case OCLASS_USER_MAPPING
:
13466 case OCLASS_DEFACL
:
13467 case OCLASS_EXTENSION
:
13468 case OCLASS_EVENT_TRIGGER
:
13469 case OCLASS_PARAMETER_ACL
:
13470 case OCLASS_PUBLICATION
:
13471 case OCLASS_PUBLICATION_NAMESPACE
:
13472 case OCLASS_PUBLICATION_REL
:
13473 case OCLASS_SUBSCRIPTION
:
13474 case OCLASS_TRANSFORM
:
13477 * We don't expect any of these sorts of objects to depend on
13480 elog(ERROR
, "unexpected object depending on column: %s",
13481 getObjectDescription(&foundObject
, false));
13485 * There's intentionally no default: case here; we want the
13486 * compiler to warn if a new OCLASS hasn't been handled above.
13491 systable_endscan(scan
);
13494 * Now scan for dependencies of this column on other things. The only
13495 * things we should find are the dependency on the column datatype and
13496 * possibly a collation dependency. Those can be removed.
13498 ScanKeyInit(&key
[0],
13499 Anum_pg_depend_classid
,
13500 BTEqualStrategyNumber
, F_OIDEQ
,
13501 ObjectIdGetDatum(RelationRelationId
));
13502 ScanKeyInit(&key
[1],
13503 Anum_pg_depend_objid
,
13504 BTEqualStrategyNumber
, F_OIDEQ
,
13505 ObjectIdGetDatum(RelationGetRelid(rel
)));
13506 ScanKeyInit(&key
[2],
13507 Anum_pg_depend_objsubid
,
13508 BTEqualStrategyNumber
, F_INT4EQ
,
13509 Int32GetDatum((int32
) attnum
));
13511 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
13514 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13516 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13517 ObjectAddress foundObject
;
13519 foundObject
.classId
= foundDep
->refclassid
;
13520 foundObject
.objectId
= foundDep
->refobjid
;
13521 foundObject
.objectSubId
= foundDep
->refobjsubid
;
13523 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
13524 elog(ERROR
, "found unexpected dependency type '%c'",
13525 foundDep
->deptype
);
13526 if (!(foundDep
->refclassid
== TypeRelationId
&&
13527 foundDep
->refobjid
== attTup
->atttypid
) &&
13528 !(foundDep
->refclassid
== CollationRelationId
&&
13529 foundDep
->refobjid
== attTup
->attcollation
))
13530 elog(ERROR
, "found unexpected dependency for column: %s",
13531 getObjectDescription(&foundObject
, false));
13533 CatalogTupleDelete(depRel
, &depTup
->t_self
);
13536 systable_endscan(scan
);
13538 table_close(depRel
, RowExclusiveLock
);
13541 * Here we go --- change the recorded column type and collation. (Note
13542 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13543 * fix up the missing value if any.
13545 if (attTup
->atthasmissing
)
13550 /* if rewrite is true the missing value should already be cleared */
13551 Assert(tab
->rewrite
== 0);
13553 /* Get the missing value datum */
13554 missingval
= heap_getattr(heapTup
,
13555 Anum_pg_attribute_attmissingval
,
13556 attrelation
->rd_att
,
13559 /* if it's a null array there is nothing to do */
13564 * Get the datum out of the array and repack it in a new array
13565 * built with the new type data. We assume that since the table
13566 * doesn't need rewriting, the actual Datum doesn't need to be
13567 * changed, only the array metadata.
13572 Datum valuesAtt
[Natts_pg_attribute
] = {0};
13573 bool nullsAtt
[Natts_pg_attribute
] = {0};
13574 bool replacesAtt
[Natts_pg_attribute
] = {0};
13577 missingval
= array_get_element(missingval
,
13585 missingval
= PointerGetDatum(construct_array(&missingval
,
13592 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
13593 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
13594 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
13596 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
13597 valuesAtt
, nullsAtt
, replacesAtt
);
13598 heap_freetuple(heapTup
);
13600 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13604 attTup
->atttypid
= targettype
;
13605 attTup
->atttypmod
= targettypmod
;
13606 attTup
->attcollation
= targetcollid
;
13607 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
13609 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
13610 errmsg("too many array dimensions"));
13611 attTup
->attndims
= list_length(typeName
->arrayBounds
);
13612 attTup
->attlen
= tform
->typlen
;
13613 attTup
->attbyval
= tform
->typbyval
;
13614 attTup
->attalign
= tform
->typalign
;
13615 attTup
->attstorage
= tform
->typstorage
;
13616 attTup
->attcompression
= InvalidCompressionMethod
;
13618 ReleaseSysCache(typeTuple
);
13620 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
13622 table_close(attrelation
, RowExclusiveLock
);
13624 /* Install dependencies on new datatype and collation */
13625 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
13626 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
13629 * Drop any pg_statistic entry for the column, since it's now wrong type
13631 RemoveStatistics(RelationGetRelid(rel
), attnum
);
13633 InvokeObjectPostAlterHook(RelationRelationId
,
13634 RelationGetRelid(rel
), attnum
);
13637 * Update the default, if present, by brute force --- remove and re-add
13638 * the default. Probably unsafe to take shortcuts, since the new version
13639 * may well have additional dependencies. (It's okay to do this now,
13640 * rather than after other ALTER TYPE commands, since the default won't
13641 * depend on other column types.)
13646 * If it's a GENERATED default, drop its dependency records, in
13647 * particular its INTERNAL dependency on the column, which would
13648 * otherwise cause dependency.c to refuse to perform the deletion.
13650 if (attTup
->attgenerated
)
13652 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
13654 if (!OidIsValid(attrdefoid
))
13655 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
13656 RelationGetRelid(rel
), attnum
);
13657 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
13661 * Make updates-so-far visible, particularly the new pg_attribute row
13662 * which will be updated again.
13664 CommandCounterIncrement();
13667 * We use RESTRICT here for safety, but at present we do not expect
13668 * anything to depend on the default.
13670 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
13673 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
13676 ObjectAddressSubSet(address
, RelationRelationId
,
13677 RelationGetRelid(rel
), attnum
);
13680 heap_freetuple(heapTup
);
13686 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13687 * needs to be reset.
13690 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13692 if (!get_index_isreplident(indoid
))
13695 if (tab
->replicaIdentityIndex
)
13696 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
13698 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
13702 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13705 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13707 if (!get_index_isclustered(indoid
))
13710 if (tab
->clusterOnIndex
)
13711 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
13713 tab
->clusterOnIndex
= get_rel_name(indoid
);
13717 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13718 * to be rebuilt (which we might already know).
13721 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
13724 * This de-duplication check is critical for two independent reasons: we
13725 * mustn't try to recreate the same constraint twice, and if a constraint
13726 * depends on more than one column whose type is to be altered, we must
13727 * capture its definition string before applying any of the column type
13728 * changes. ruleutils.c will get confused if we ask again later.
13730 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
13732 /* OK, capture the constraint's existing definition string */
13733 char *defstring
= pg_get_constraintdef_command(conoid
);
13736 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
13738 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
13742 * For the index of a constraint, if any, remember if it is used for
13743 * the table's replica identity or if it is a clustered index, so that
13744 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13745 * those properties.
13747 indoid
= get_constraint_index(conoid
);
13748 if (OidIsValid(indoid
))
13750 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13751 RememberClusterOnForRebuilding(indoid
, tab
);
13757 * Subroutine for ATExecAlterColumnType: remember that an index needs
13758 * to be rebuilt (which we might already know).
13761 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13764 * This de-duplication check is critical for two independent reasons: we
13765 * mustn't try to recreate the same index twice, and if an index depends
13766 * on more than one column whose type is to be altered, we must capture
13767 * its definition string before applying any of the column type changes.
13768 * ruleutils.c will get confused if we ask again later.
13770 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
13773 * Before adding it as an index-to-rebuild, we'd better see if it
13774 * belongs to a constraint, and if so rebuild the constraint instead.
13775 * Typically this check fails, because constraint indexes normally
13776 * have only dependencies on their constraint. But it's possible for
13777 * such an index to also have direct dependencies on table columns,
13778 * for example with a partial exclusion constraint.
13780 Oid conoid
= get_index_constraint(indoid
);
13782 if (OidIsValid(conoid
))
13784 RememberConstraintForRebuilding(conoid
, tab
);
13788 /* OK, capture the index's existing definition string */
13789 char *defstring
= pg_get_indexdef_string(indoid
);
13791 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
13793 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
13797 * Remember if this index is used for the table's replica identity
13798 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13799 * can queue up commands necessary to restore those properties.
13801 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13802 RememberClusterOnForRebuilding(indoid
, tab
);
13808 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13809 * needs to be rebuilt (which we might already know).
13812 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
13815 * This de-duplication check is critical for two independent reasons: we
13816 * mustn't try to recreate the same statistics object twice, and if the
13817 * statistics object depends on more than one column whose type is to be
13818 * altered, we must capture its definition string before applying any of
13819 * the type changes. ruleutils.c will get confused if we ask again later.
13821 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
13823 /* OK, capture the statistics object's existing definition string */
13824 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
13826 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
13828 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
13834 * Cleanup after we've finished all the ALTER TYPE operations for a
13835 * particular relation. We have to drop and recreate all the indexes
13836 * and constraints that depend on the altered columns. We do the
13837 * actual dropping here, but re-creation is managed by adding work
13838 * queue entries to do those steps later.
13841 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
13844 ObjectAddresses
*objects
;
13845 ListCell
*def_item
;
13846 ListCell
*oid_item
;
13849 * Collect all the constraints and indexes to drop so we can process them
13850 * in a single call. That way we don't have to worry about dependencies
13853 objects
= new_object_addresses();
13856 * Re-parse the index and constraint definitions, and attach them to the
13857 * appropriate work queue entries. We do this before dropping because in
13858 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13859 * lock on the table the constraint is attached to, and we need to get
13860 * that before reparsing/dropping.
13862 * We can't rely on the output of deparsing to tell us which relation to
13863 * operate on, because concurrent activity might have made the name
13864 * resolve differently. Instead, we've got to use the OID of the
13865 * constraint or index we're processing to figure out which relation to
13868 forboth(oid_item
, tab
->changedConstraintOids
,
13869 def_item
, tab
->changedConstraintDefs
)
13871 Oid oldId
= lfirst_oid(oid_item
);
13873 Form_pg_constraint con
;
13879 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
13880 if (!HeapTupleIsValid(tup
)) /* should not happen */
13881 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
13882 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
13883 if (OidIsValid(con
->conrelid
))
13884 relid
= con
->conrelid
;
13887 /* must be a domain constraint */
13888 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
13889 if (!OidIsValid(relid
))
13890 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
13892 confrelid
= con
->confrelid
;
13893 contype
= con
->contype
;
13894 conislocal
= con
->conislocal
;
13895 ReleaseSysCache(tup
);
13897 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
13898 add_exact_object_address(&obj
, objects
);
13901 * If the constraint is inherited (only), we don't want to inject a
13902 * new definition here; it'll get recreated when
13903 * ATAddCheckNNConstraint recurses from adding the parent table's
13904 * constraint. But we had to carry the info this far so that we can
13905 * drop the constraint below.
13911 * When rebuilding an FK constraint that references the table we're
13912 * modifying, we might not yet have any lock on the FK's table, so get
13913 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13914 * step, so there's no value in asking for anything weaker.
13916 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
13917 LockRelationOid(relid
, AccessExclusiveLock
);
13919 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
13920 (char *) lfirst(def_item
),
13921 wqueue
, lockmode
, tab
->rewrite
);
13923 forboth(oid_item
, tab
->changedIndexOids
,
13924 def_item
, tab
->changedIndexDefs
)
13926 Oid oldId
= lfirst_oid(oid_item
);
13929 relid
= IndexGetRelation(oldId
, false);
13930 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13931 (char *) lfirst(def_item
),
13932 wqueue
, lockmode
, tab
->rewrite
);
13934 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
13935 add_exact_object_address(&obj
, objects
);
13938 /* add dependencies for new statistics */
13939 forboth(oid_item
, tab
->changedStatisticsOids
,
13940 def_item
, tab
->changedStatisticsDefs
)
13942 Oid oldId
= lfirst_oid(oid_item
);
13945 relid
= StatisticsGetRelation(oldId
, false);
13946 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13947 (char *) lfirst(def_item
),
13948 wqueue
, lockmode
, tab
->rewrite
);
13950 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
13951 add_exact_object_address(&obj
, objects
);
13955 * Queue up command to restore replica identity index marking
13957 if (tab
->replicaIdentityIndex
)
13959 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13960 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
13962 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
13963 subcmd
->name
= tab
->replicaIdentityIndex
;
13964 cmd
->subtype
= AT_ReplicaIdentity
;
13965 cmd
->def
= (Node
*) subcmd
;
13967 /* do it after indexes and constraints */
13968 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13969 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13973 * Queue up command to restore marking of index used for cluster.
13975 if (tab
->clusterOnIndex
)
13977 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13979 cmd
->subtype
= AT_ClusterOn
;
13980 cmd
->name
= tab
->clusterOnIndex
;
13982 /* do it after indexes and constraints */
13983 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13984 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13988 * It should be okay to use DROP_RESTRICT here, since nothing else should
13989 * be depending on these objects.
13991 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
13993 free_object_addresses(objects
);
13996 * The objects will get recreated during subsequent passes over the work
14002 * Parse the previously-saved definition string for a constraint, index or
14003 * statistics object against the newly-established column data type(s), and
14004 * queue up the resulting command parsetrees for execution.
14006 * This might fail if, for example, you have a WHERE clause that uses an
14007 * operator that's not available for the new column type.
14010 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
14011 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
14013 List
*raw_parsetree_list
;
14014 List
*querytree_list
;
14015 ListCell
*list_item
;
14019 * We expect that we will get only ALTER TABLE and CREATE INDEX
14020 * statements. Hence, there is no need to pass them through
14021 * parse_analyze_*() or the rewriter, but instead we need to pass them
14022 * through parse_utilcmd.c to make them ready for execution.
14024 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
14025 querytree_list
= NIL
;
14026 foreach(list_item
, raw_parsetree_list
)
14028 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
14029 Node
*stmt
= rs
->stmt
;
14031 if (IsA(stmt
, IndexStmt
))
14032 querytree_list
= lappend(querytree_list
,
14033 transformIndexStmt(oldRelId
,
14034 (IndexStmt
*) stmt
,
14036 else if (IsA(stmt
, AlterTableStmt
))
14041 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
14042 (AlterTableStmt
*) stmt
,
14046 querytree_list
= list_concat(querytree_list
, beforeStmts
);
14047 querytree_list
= lappend(querytree_list
, stmt
);
14048 querytree_list
= list_concat(querytree_list
, afterStmts
);
14050 else if (IsA(stmt
, CreateStatsStmt
))
14051 querytree_list
= lappend(querytree_list
,
14052 transformStatsStmt(oldRelId
,
14053 (CreateStatsStmt
*) stmt
,
14056 querytree_list
= lappend(querytree_list
, stmt
);
14059 /* Caller should already have acquired whatever lock we need. */
14060 rel
= relation_open(oldRelId
, NoLock
);
14063 * Attach each generated command to the proper place in the work queue.
14064 * Note this could result in creation of entirely new work-queue entries.
14066 * Also note that we have to tweak the command subtypes, because it turns
14067 * out that re-creation of indexes and constraints has to act a bit
14068 * differently from initial creation.
14070 foreach(list_item
, querytree_list
)
14072 Node
*stm
= (Node
*) lfirst(list_item
);
14073 AlteredTableInfo
*tab
;
14075 tab
= ATGetQueueEntry(wqueue
, rel
);
14077 if (IsA(stm
, IndexStmt
))
14079 IndexStmt
*stmt
= (IndexStmt
*) stm
;
14080 AlterTableCmd
*newcmd
;
14083 TryReuseIndex(oldId
, stmt
);
14084 stmt
->reset_default_tblspc
= true;
14085 /* keep the index's comment */
14086 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
14088 newcmd
= makeNode(AlterTableCmd
);
14089 newcmd
->subtype
= AT_ReAddIndex
;
14090 newcmd
->def
= (Node
*) stmt
;
14091 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14092 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
14094 else if (IsA(stm
, AlterTableStmt
))
14096 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
14099 foreach(lcmd
, stmt
->cmds
)
14101 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
14103 if (cmd
->subtype
== AT_AddIndex
)
14105 IndexStmt
*indstmt
;
14108 indstmt
= castNode(IndexStmt
, cmd
->def
);
14109 indoid
= get_constraint_index(oldId
);
14112 TryReuseIndex(indoid
, indstmt
);
14113 /* keep any comment on the index */
14114 indstmt
->idxcomment
= GetComment(indoid
,
14115 RelationRelationId
, 0);
14116 indstmt
->reset_default_tblspc
= true;
14118 cmd
->subtype
= AT_ReAddIndex
;
14119 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14120 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
14122 /* recreate any comment on the constraint */
14123 RebuildConstraintComment(tab
,
14130 else if (cmd
->subtype
== AT_AddConstraint
)
14132 Constraint
*con
= castNode(Constraint
, cmd
->def
);
14134 con
->old_pktable_oid
= refRelId
;
14135 /* rewriting neither side of a FK */
14136 if (con
->contype
== CONSTR_FOREIGN
&&
14137 !rewrite
&& tab
->rewrite
== 0)
14138 TryReuseForeignKey(oldId
, con
);
14139 con
->reset_default_tblspc
= true;
14140 cmd
->subtype
= AT_ReAddConstraint
;
14141 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14142 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14144 /* recreate any comment on the constraint */
14145 RebuildConstraintComment(tab
,
14146 AT_PASS_OLD_CONSTR
,
14152 else if (cmd
->subtype
== AT_SetAttNotNull
)
14155 * The parser will create AT_AttSetNotNull subcommands for
14156 * columns of PRIMARY KEY indexes/constraints, but we need
14157 * not do anything with them here, because the columns'
14158 * NOT NULL marks will already have been propagated into
14159 * the new table definition.
14163 elog(ERROR
, "unexpected statement subtype: %d",
14164 (int) cmd
->subtype
);
14167 else if (IsA(stm
, AlterDomainStmt
))
14169 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
14171 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
14173 Constraint
*con
= castNode(Constraint
, stmt
->def
);
14174 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14176 cmd
->subtype
= AT_ReAddDomainConstraint
;
14177 cmd
->def
= (Node
*) stmt
;
14178 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14179 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14181 /* recreate any comment on the constraint */
14182 RebuildConstraintComment(tab
,
14183 AT_PASS_OLD_CONSTR
,
14190 elog(ERROR
, "unexpected statement subtype: %d",
14191 (int) stmt
->subtype
);
14193 else if (IsA(stm
, CreateStatsStmt
))
14195 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
14196 AlterTableCmd
*newcmd
;
14198 /* keep the statistics object's comment */
14199 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
14201 newcmd
= makeNode(AlterTableCmd
);
14202 newcmd
->subtype
= AT_ReAddStatistics
;
14203 newcmd
->def
= (Node
*) stmt
;
14204 tab
->subcmds
[AT_PASS_MISC
] =
14205 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
14208 elog(ERROR
, "unexpected statement type: %d",
14209 (int) nodeTag(stm
));
14212 relation_close(rel
, NoLock
);
14216 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14217 * for a table or domain constraint that is being rebuilt.
14219 * objid is the OID of the constraint.
14220 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14221 * as a string list) for a domain constraint.
14222 * (We could dig that info, as well as the conname, out of the pg_constraint
14223 * entry; but callers already have them so might as well pass them.)
14226 RebuildConstraintComment(AlteredTableInfo
*tab
, int pass
, Oid objid
,
14227 Relation rel
, List
*domname
,
14228 const char *conname
)
14232 AlterTableCmd
*newcmd
;
14234 /* Look for comment for object wanted, and leave if none */
14235 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
14236 if (comment_str
== NULL
)
14239 /* Build CommentStmt node, copying all input data for safety */
14240 cmd
= makeNode(CommentStmt
);
14243 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
14244 cmd
->object
= (Node
*)
14245 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
14246 makeString(pstrdup(RelationGetRelationName(rel
))),
14247 makeString(pstrdup(conname
)));
14251 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
14252 cmd
->object
= (Node
*)
14253 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
14254 makeString(pstrdup(conname
)));
14256 cmd
->comment
= comment_str
;
14258 /* Append it to list of commands */
14259 newcmd
= makeNode(AlterTableCmd
);
14260 newcmd
->subtype
= AT_ReAddComment
;
14261 newcmd
->def
= (Node
*) cmd
;
14262 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
14266 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14267 * for the real analysis, then mutates the IndexStmt based on that verdict.
14270 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
14272 if (CheckIndexCompatible(oldId
,
14273 stmt
->accessMethod
,
14275 stmt
->excludeOpNames
))
14277 Relation irel
= index_open(oldId
, NoLock
);
14279 /* If it's a partitioned index, there is no storage to share. */
14280 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
14282 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
14283 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
14284 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
14286 index_close(irel
, NoLock
);
14291 * Subroutine for ATPostAlterTypeParse().
14293 * Stash the old P-F equality operator into the Constraint node, for possible
14294 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14295 * this constraint can be skipped.
14298 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
14307 Assert(con
->contype
== CONSTR_FOREIGN
);
14308 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
14310 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14311 if (!HeapTupleIsValid(tup
)) /* should not happen */
14312 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14314 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
14315 Anum_pg_constraint_conpfeqop
);
14316 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
14317 numkeys
= ARR_DIMS(arr
)[0];
14318 /* test follows the one in ri_FetchConstraintInfo() */
14319 if (ARR_NDIM(arr
) != 1 ||
14320 ARR_HASNULL(arr
) ||
14321 ARR_ELEMTYPE(arr
) != OIDOID
)
14322 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
14323 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
14325 /* stash a List of the operator Oids in our Constraint node */
14326 for (i
= 0; i
< numkeys
; i
++)
14327 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
14329 ReleaseSysCache(tup
);
14333 * ALTER COLUMN .. OPTIONS ( ... )
14335 * Returns the address of the modified column
14337 static ObjectAddress
14338 ATExecAlterColumnGenericOptions(Relation rel
,
14339 const char *colName
,
14345 ForeignServer
*server
;
14346 ForeignDataWrapper
*fdw
;
14348 HeapTuple newtuple
;
14350 Datum repl_val
[Natts_pg_attribute
];
14351 bool repl_null
[Natts_pg_attribute
];
14352 bool repl_repl
[Natts_pg_attribute
];
14354 Form_pg_foreign_table fttableform
;
14355 Form_pg_attribute atttableform
;
14357 ObjectAddress address
;
14359 if (options
== NIL
)
14360 return InvalidObjectAddress
;
14362 /* First, determine FDW validator associated to the foreign table. */
14363 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
14364 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
14365 if (!HeapTupleIsValid(tuple
))
14367 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14368 errmsg("foreign table \"%s\" does not exist",
14369 RelationGetRelationName(rel
))));
14370 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
14371 server
= GetForeignServer(fttableform
->ftserver
);
14372 fdw
= GetForeignDataWrapper(server
->fdwid
);
14374 table_close(ftrel
, AccessShareLock
);
14375 ReleaseSysCache(tuple
);
14377 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
14378 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
14379 if (!HeapTupleIsValid(tuple
))
14381 (errcode(ERRCODE_UNDEFINED_COLUMN
),
14382 errmsg("column \"%s\" of relation \"%s\" does not exist",
14383 colName
, RelationGetRelationName(rel
))));
14385 /* Prevent them from altering a system attribute */
14386 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
14387 attnum
= atttableform
->attnum
;
14390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14391 errmsg("cannot alter system column \"%s\"", colName
)));
14394 /* Initialize buffers for new tuple values */
14395 memset(repl_val
, 0, sizeof(repl_val
));
14396 memset(repl_null
, false, sizeof(repl_null
));
14397 memset(repl_repl
, false, sizeof(repl_repl
));
14399 /* Extract the current options */
14400 datum
= SysCacheGetAttr(ATTNAME
,
14402 Anum_pg_attribute_attfdwoptions
,
14405 datum
= PointerGetDatum(NULL
);
14407 /* Transform the options */
14408 datum
= transformGenericOptions(AttributeRelationId
,
14411 fdw
->fdwvalidator
);
14413 if (PointerIsValid(DatumGetPointer(datum
)))
14414 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
14416 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14418 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14420 /* Everything looks good - update the tuple */
14422 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
14423 repl_val
, repl_null
, repl_repl
);
14425 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
14427 InvokeObjectPostAlterHook(RelationRelationId
,
14428 RelationGetRelid(rel
),
14429 atttableform
->attnum
);
14430 ObjectAddressSubSet(address
, RelationRelationId
,
14431 RelationGetRelid(rel
), attnum
);
14433 ReleaseSysCache(tuple
);
14435 table_close(attrel
, RowExclusiveLock
);
14437 heap_freetuple(newtuple
);
14443 * ALTER TABLE OWNER
14445 * recursing is true if we are recursing from a table to its indexes,
14446 * sequences, or toast table. We don't allow the ownership of those things to
14447 * be changed separately from the parent table. Also, we can skip permission
14448 * checks (this is necessary not just an optimization, else we'd fail to
14449 * handle toast tables properly).
14451 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14452 * free-standing composite type.
14455 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
14457 Relation target_rel
;
14458 Relation class_rel
;
14460 Form_pg_class tuple_class
;
14463 * Get exclusive lock till end of transaction on the target table. Use
14464 * relation_open so that we can work on indexes and sequences.
14466 target_rel
= relation_open(relationOid
, lockmode
);
14468 /* Get its pg_class tuple, too */
14469 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
14471 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
14472 if (!HeapTupleIsValid(tuple
))
14473 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
14474 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
14476 /* Can we change the ownership of this tuple? */
14477 switch (tuple_class
->relkind
)
14479 case RELKIND_RELATION
:
14481 case RELKIND_MATVIEW
:
14482 case RELKIND_FOREIGN_TABLE
:
14483 case RELKIND_PARTITIONED_TABLE
:
14484 /* ok to change owner */
14486 case RELKIND_INDEX
:
14490 * Because ALTER INDEX OWNER used to be allowed, and in fact
14491 * is generated by old versions of pg_dump, we give a warning
14492 * and do nothing rather than erroring out. Also, to avoid
14493 * unnecessary chatter while restoring those old dumps, say
14494 * nothing at all if the command would be a no-op anyway.
14496 if (tuple_class
->relowner
!= newOwnerId
)
14498 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14499 errmsg("cannot change owner of index \"%s\"",
14500 NameStr(tuple_class
->relname
)),
14501 errhint("Change the ownership of the index's table instead.")));
14502 /* quick hack to exit via the no-op path */
14503 newOwnerId
= tuple_class
->relowner
;
14506 case RELKIND_PARTITIONED_INDEX
:
14510 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14511 errmsg("cannot change owner of index \"%s\"",
14512 NameStr(tuple_class
->relname
)),
14513 errhint("Change the ownership of the index's table instead.")));
14515 case RELKIND_SEQUENCE
:
14517 tuple_class
->relowner
!= newOwnerId
)
14519 /* if it's an owned sequence, disallow changing it by itself */
14523 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
14524 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
14526 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14527 errmsg("cannot change owner of sequence \"%s\"",
14528 NameStr(tuple_class
->relname
)),
14529 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14530 NameStr(tuple_class
->relname
),
14531 get_rel_name(tableId
))));
14534 case RELKIND_COMPOSITE_TYPE
:
14538 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14539 errmsg("\"%s\" is a composite type",
14540 NameStr(tuple_class
->relname
)),
14541 /* translator: %s is an SQL ALTER command */
14542 errhint("Use %s instead.",
14545 case RELKIND_TOASTVALUE
:
14551 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14552 errmsg("cannot change owner of relation \"%s\"",
14553 NameStr(tuple_class
->relname
)),
14554 errdetail_relkind_not_supported(tuple_class
->relkind
)));
14558 * If the new owner is the same as the existing owner, consider the
14559 * command to have succeeded. This is for dump restoration purposes.
14561 if (tuple_class
->relowner
!= newOwnerId
)
14563 Datum repl_val
[Natts_pg_class
];
14564 bool repl_null
[Natts_pg_class
];
14565 bool repl_repl
[Natts_pg_class
];
14569 HeapTuple newtuple
;
14571 /* skip permission checks when recursing to index or toast table */
14574 /* Superusers can always do it */
14577 Oid namespaceOid
= tuple_class
->relnamespace
;
14578 AclResult aclresult
;
14580 /* Otherwise, must be owner of the existing object */
14581 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
14582 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
14583 RelationGetRelationName(target_rel
));
14585 /* Must be able to become new owner */
14586 check_can_set_role(GetUserId(), newOwnerId
);
14588 /* New owner must have CREATE privilege on namespace */
14589 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
14591 if (aclresult
!= ACLCHECK_OK
)
14592 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
14593 get_namespace_name(namespaceOid
));
14597 memset(repl_null
, false, sizeof(repl_null
));
14598 memset(repl_repl
, false, sizeof(repl_repl
));
14600 repl_repl
[Anum_pg_class_relowner
- 1] = true;
14601 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
14604 * Determine the modified ACL for the new owner. This is only
14605 * necessary when the ACL is non-null.
14607 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
14608 Anum_pg_class_relacl
,
14612 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
14613 tuple_class
->relowner
, newOwnerId
);
14614 repl_repl
[Anum_pg_class_relacl
- 1] = true;
14615 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
14618 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
14620 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
14622 heap_freetuple(newtuple
);
14625 * We must similarly update any per-column ACLs to reflect the new
14626 * owner; for neatness reasons that's split out as a subroutine.
14628 change_owner_fix_column_acls(relationOid
,
14629 tuple_class
->relowner
,
14633 * Update owner dependency reference, if any. A composite type has
14634 * none, because it's tracked for the pg_type entry instead of here;
14635 * indexes and TOAST tables don't have their own entries either.
14637 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
14638 tuple_class
->relkind
!= RELKIND_INDEX
&&
14639 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
14640 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
14641 changeDependencyOnOwner(RelationRelationId
, relationOid
,
14645 * Also change the ownership of the table's row type, if it has one
14647 if (OidIsValid(tuple_class
->reltype
))
14648 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
14651 * If we are operating on a table or materialized view, also change
14652 * the ownership of any indexes and sequences that belong to the
14653 * relation, as well as its toast table (if it has one).
14655 if (tuple_class
->relkind
== RELKIND_RELATION
||
14656 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
14657 tuple_class
->relkind
== RELKIND_MATVIEW
||
14658 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
14660 List
*index_oid_list
;
14663 /* Find all the indexes belonging to this relation */
14664 index_oid_list
= RelationGetIndexList(target_rel
);
14666 /* For each index, recursively change its ownership */
14667 foreach(i
, index_oid_list
)
14668 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
14670 list_free(index_oid_list
);
14673 /* If it has a toast table, recurse to change its ownership */
14674 if (tuple_class
->reltoastrelid
!= InvalidOid
)
14675 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
14678 /* If it has dependent sequences, recurse to change them too */
14679 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
14682 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
14684 ReleaseSysCache(tuple
);
14685 table_close(class_rel
, RowExclusiveLock
);
14686 relation_close(target_rel
, NoLock
);
14690 * change_owner_fix_column_acls
14692 * Helper function for ATExecChangeOwner. Scan the columns of the table
14693 * and fix any non-null column ACLs to reflect the new owner.
14696 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
14698 Relation attRelation
;
14700 ScanKeyData key
[1];
14701 HeapTuple attributeTuple
;
14703 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
14704 ScanKeyInit(&key
[0],
14705 Anum_pg_attribute_attrelid
,
14706 BTEqualStrategyNumber
, F_OIDEQ
,
14707 ObjectIdGetDatum(relationOid
));
14708 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
14709 true, NULL
, 1, key
);
14710 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
14712 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
14713 Datum repl_val
[Natts_pg_attribute
];
14714 bool repl_null
[Natts_pg_attribute
];
14715 bool repl_repl
[Natts_pg_attribute
];
14719 HeapTuple newtuple
;
14721 /* Ignore dropped columns */
14722 if (att
->attisdropped
)
14725 aclDatum
= heap_getattr(attributeTuple
,
14726 Anum_pg_attribute_attacl
,
14727 RelationGetDescr(attRelation
),
14729 /* Null ACLs do not require changes */
14733 memset(repl_null
, false, sizeof(repl_null
));
14734 memset(repl_repl
, false, sizeof(repl_repl
));
14736 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
14737 oldOwnerId
, newOwnerId
);
14738 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
14739 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
14741 newtuple
= heap_modify_tuple(attributeTuple
,
14742 RelationGetDescr(attRelation
),
14743 repl_val
, repl_null
, repl_repl
);
14745 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
14747 heap_freetuple(newtuple
);
14749 systable_endscan(scan
);
14750 table_close(attRelation
, RowExclusiveLock
);
14754 * change_owner_recurse_to_sequences
14756 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14757 * for sequences that are dependent on serial columns, and changes their
14761 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
14765 ScanKeyData key
[2];
14769 * SERIAL sequences are those having an auto dependency on one of the
14770 * table's columns (we don't care *which* column, exactly).
14772 depRel
= table_open(DependRelationId
, AccessShareLock
);
14774 ScanKeyInit(&key
[0],
14775 Anum_pg_depend_refclassid
,
14776 BTEqualStrategyNumber
, F_OIDEQ
,
14777 ObjectIdGetDatum(RelationRelationId
));
14778 ScanKeyInit(&key
[1],
14779 Anum_pg_depend_refobjid
,
14780 BTEqualStrategyNumber
, F_OIDEQ
,
14781 ObjectIdGetDatum(relationOid
));
14782 /* we leave refobjsubid unspecified */
14784 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
14787 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
14789 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
14792 /* skip dependencies other than auto dependencies on columns */
14793 if (depForm
->refobjsubid
== 0 ||
14794 depForm
->classid
!= RelationRelationId
||
14795 depForm
->objsubid
!= 0 ||
14796 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
14799 /* Use relation_open just in case it's an index */
14800 seqRel
= relation_open(depForm
->objid
, lockmode
);
14802 /* skip non-sequence relations */
14803 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
14805 /* No need to keep the lock */
14806 relation_close(seqRel
, lockmode
);
14810 /* We don't need to close the sequence while we alter it. */
14811 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
14813 /* Now we can close it. Keep the lock till end of transaction. */
14814 relation_close(seqRel
, NoLock
);
14817 systable_endscan(scan
);
14819 relation_close(depRel
, AccessShareLock
);
14823 * ALTER TABLE CLUSTER ON
14825 * The only thing we have to do is to change the indisclustered bits.
14827 * Return the address of the new clustering index.
14829 static ObjectAddress
14830 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
14833 ObjectAddress address
;
14835 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
14837 if (!OidIsValid(indexOid
))
14839 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14840 errmsg("index \"%s\" for table \"%s\" does not exist",
14841 indexName
, RelationGetRelationName(rel
))));
14843 /* Check index is valid to cluster on */
14844 check_index_is_clusterable(rel
, indexOid
, lockmode
);
14846 /* And do the work */
14847 mark_index_clustered(rel
, indexOid
, false);
14849 ObjectAddressSet(address
,
14850 RelationRelationId
, indexOid
);
14856 * ALTER TABLE SET WITHOUT CLUSTER
14858 * We have to find any indexes on the table that have indisclustered bit
14859 * set and turn it off.
14862 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
14864 mark_index_clustered(rel
, InvalidOid
, false);
14868 * Preparation phase for SET ACCESS METHOD
14870 * Check that access method exists. If it is the same as the table's current
14871 * access method, it is a no-op. Otherwise, a table rewrite is necessary.
14874 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
14878 /* Check that the table access method exists */
14879 amoid
= get_table_am_oid(amname
, false);
14881 if (rel
->rd_rel
->relam
== amoid
)
14884 /* Save info for Phase 3 to do the real work */
14885 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
14886 tab
->newAccessMethod
= amoid
;
14890 * ALTER TABLE SET TABLESPACE
14893 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
14897 /* Check that the tablespace exists */
14898 tablespaceId
= get_tablespace_oid(tablespacename
, false);
14900 /* Check permissions except when moving to database's default */
14901 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
14903 AclResult aclresult
;
14905 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
14906 if (aclresult
!= ACLCHECK_OK
)
14907 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
14910 /* Save info for Phase 3 to do the real work */
14911 if (OidIsValid(tab
->newTableSpace
))
14913 (errcode(ERRCODE_SYNTAX_ERROR
),
14914 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14916 tab
->newTableSpace
= tablespaceId
;
14920 * Set, reset, or replace reloptions.
14923 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
14929 HeapTuple newtuple
;
14933 Datum repl_val
[Natts_pg_class
];
14934 bool repl_null
[Natts_pg_class
];
14935 bool repl_repl
[Natts_pg_class
];
14936 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
14938 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
14939 return; /* nothing to do */
14941 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
14943 /* Fetch heap tuple */
14944 relid
= RelationGetRelid(rel
);
14945 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
14946 if (!HeapTupleIsValid(tuple
))
14947 elog(ERROR
, "cache lookup failed for relation %u", relid
);
14949 if (operation
== AT_ReplaceRelOptions
)
14952 * If we're supposed to replace the reloptions list, we just pretend
14953 * there were none before.
14960 /* Get the old reloptions */
14961 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
14965 /* Generate new proposed reloptions (text array) */
14966 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
14967 defList
, NULL
, validnsps
, false,
14968 operation
== AT_ResetRelOptions
);
14971 switch (rel
->rd_rel
->relkind
)
14973 case RELKIND_RELATION
:
14974 case RELKIND_TOASTVALUE
:
14975 case RELKIND_MATVIEW
:
14976 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
14978 case RELKIND_PARTITIONED_TABLE
:
14979 (void) partitioned_table_reloptions(newOptions
, true);
14982 (void) view_reloptions(newOptions
, true);
14984 case RELKIND_INDEX
:
14985 case RELKIND_PARTITIONED_INDEX
:
14986 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
14990 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14991 errmsg("cannot set options for relation \"%s\"",
14992 RelationGetRelationName(rel
)),
14993 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
14997 /* Special-case validation of view options */
14998 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
15000 Query
*view_query
= get_view_query(rel
);
15001 List
*view_options
= untransformRelOptions(newOptions
);
15003 bool check_option
= false;
15005 foreach(cell
, view_options
)
15007 DefElem
*defel
= (DefElem
*) lfirst(cell
);
15009 if (strcmp(defel
->defname
, "check_option") == 0)
15010 check_option
= true;
15014 * If the check option is specified, look to see if the view is
15015 * actually auto-updatable or not.
15019 const char *view_updatable_error
=
15020 view_query_is_auto_updatable(view_query
, true);
15022 if (view_updatable_error
)
15024 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15025 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15026 errhint("%s", _(view_updatable_error
))));
15031 * All we need do here is update the pg_class row; the new options will be
15032 * propagated into relcaches during post-commit cache inval.
15034 memset(repl_val
, 0, sizeof(repl_val
));
15035 memset(repl_null
, false, sizeof(repl_null
));
15036 memset(repl_repl
, false, sizeof(repl_repl
));
15038 if (newOptions
!= (Datum
) 0)
15039 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15041 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15043 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15045 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15046 repl_val
, repl_null
, repl_repl
);
15048 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15050 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15052 heap_freetuple(newtuple
);
15054 ReleaseSysCache(tuple
);
15056 /* repeat the whole exercise for the toast table, if there's one */
15057 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
15060 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
15062 toastrel
= table_open(toastid
, lockmode
);
15064 /* Fetch heap tuple */
15065 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
15066 if (!HeapTupleIsValid(tuple
))
15067 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
15069 if (operation
== AT_ReplaceRelOptions
)
15072 * If we're supposed to replace the reloptions list, we just
15073 * pretend there were none before.
15080 /* Get the old reloptions */
15081 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15085 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15086 defList
, "toast", validnsps
, false,
15087 operation
== AT_ResetRelOptions
);
15089 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
15091 memset(repl_val
, 0, sizeof(repl_val
));
15092 memset(repl_null
, false, sizeof(repl_null
));
15093 memset(repl_repl
, false, sizeof(repl_repl
));
15095 if (newOptions
!= (Datum
) 0)
15096 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15098 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15100 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15102 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15103 repl_val
, repl_null
, repl_repl
);
15105 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15107 InvokeObjectPostAlterHookArg(RelationRelationId
,
15108 RelationGetRelid(toastrel
), 0,
15111 heap_freetuple(newtuple
);
15113 ReleaseSysCache(tuple
);
15115 table_close(toastrel
, NoLock
);
15118 table_close(pgclass
, RowExclusiveLock
);
15122 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15123 * rewriting to be done, so we just want to copy the data as fast as possible.
15126 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
15130 RelFileNumber newrelfilenumber
;
15131 RelFileLocator newrlocator
;
15132 List
*reltoastidxids
= NIL
;
15136 * Need lock here in case we are recursing to toast table or index
15138 rel
= relation_open(tableOid
, lockmode
);
15140 /* Check first if relation can be moved to new tablespace */
15141 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15143 InvokeObjectPostAlterHook(RelationRelationId
,
15144 RelationGetRelid(rel
), 0);
15145 relation_close(rel
, NoLock
);
15149 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
15150 /* Fetch the list of indexes on toast relation if necessary */
15151 if (OidIsValid(reltoastrelid
))
15153 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
15155 reltoastidxids
= RelationGetIndexList(toastRel
);
15156 relation_close(toastRel
, lockmode
);
15160 * Relfilenumbers are not unique in databases across tablespaces, so we
15161 * need to allocate a new one in the new tablespace.
15163 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
15164 rel
->rd_rel
->relpersistence
);
15166 /* Open old and new relation */
15167 newrlocator
= rel
->rd_locator
;
15168 newrlocator
.relNumber
= newrelfilenumber
;
15169 newrlocator
.spcOid
= newTableSpace
;
15171 /* hand off to AM to actually create new rel storage and copy the data */
15172 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
15174 index_copy_data(rel
, newrlocator
);
15178 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
15179 table_relation_copy_data(rel
, &newrlocator
);
15183 * Update the pg_class row.
15185 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15186 * executed on pg_class or its indexes (the above copy wouldn't contain
15187 * the updated pg_class entry), but that's forbidden with
15188 * CheckRelationTableSpaceMove().
15190 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
15192 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15194 RelationAssumeNewRelfilelocator(rel
);
15196 relation_close(rel
, NoLock
);
15198 /* Make sure the reltablespace change is visible */
15199 CommandCounterIncrement();
15201 /* Move associated toast relation and/or indexes, too */
15202 if (OidIsValid(reltoastrelid
))
15203 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
15204 foreach(lc
, reltoastidxids
)
15205 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
15208 list_free(reltoastidxids
);
15212 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15213 * storage that have an interest in preserving tablespace.
15215 * Since these have no storage the tablespace can be updated with a simple
15216 * metadata only operation to update the tablespace.
15219 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
15222 * Shouldn't be called on relations having storage; these are processed in
15225 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15227 /* check if relation can be moved to its new tablespace */
15228 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15230 InvokeObjectPostAlterHook(RelationRelationId
,
15231 RelationGetRelid(rel
),
15236 /* Update can be done, so change reltablespace */
15237 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
15239 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15241 /* Make sure the reltablespace change is visible */
15242 CommandCounterIncrement();
15246 * Alter Table ALL ... SET TABLESPACE
15248 * Allows a user to move all objects of some type in a given tablespace in the
15249 * current database to another tablespace. Objects can be chosen based on the
15250 * owner of the object also, to allow users to move only their objects.
15251 * The user must have CREATE rights on the new tablespace, as usual. The main
15252 * permissions handling is done by the lower-level table move function.
15254 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15255 * lock can't be acquired then we ereport(ERROR).
15258 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
15260 List
*relations
= NIL
;
15262 ScanKeyData key
[1];
15264 TableScanDesc scan
;
15266 Oid orig_tablespaceoid
;
15267 Oid new_tablespaceoid
;
15268 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
15270 /* Ensure we were not asked to move something we can't */
15271 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
15272 stmt
->objtype
!= OBJECT_MATVIEW
)
15274 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15275 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15277 /* Get the orig and new tablespace OIDs */
15278 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
15279 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
15281 /* Can't move shared relations in to or out of pg_global */
15282 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15283 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
15284 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
15286 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15287 errmsg("cannot move relations in to or out of pg_global tablespace")));
15290 * Must have CREATE rights on the new tablespace, unless it is the
15291 * database default tablespace (which all users implicitly have CREATE
15294 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
15296 AclResult aclresult
;
15298 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
15300 if (aclresult
!= ACLCHECK_OK
)
15301 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
15302 get_tablespace_name(new_tablespaceoid
));
15306 * Now that the checks are done, check if we should set either to
15307 * InvalidOid because it is our database's default tablespace.
15309 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
15310 orig_tablespaceoid
= InvalidOid
;
15312 if (new_tablespaceoid
== MyDatabaseTableSpace
)
15313 new_tablespaceoid
= InvalidOid
;
15316 if (orig_tablespaceoid
== new_tablespaceoid
)
15317 return new_tablespaceoid
;
15320 * Walk the list of objects in the tablespace and move them. This will
15321 * only find objects in our database, of course.
15323 ScanKeyInit(&key
[0],
15324 Anum_pg_class_reltablespace
,
15325 BTEqualStrategyNumber
, F_OIDEQ
,
15326 ObjectIdGetDatum(orig_tablespaceoid
));
15328 rel
= table_open(RelationRelationId
, AccessShareLock
);
15329 scan
= table_beginscan_catalog(rel
, 1, key
);
15330 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
15332 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
15333 Oid relOid
= relForm
->oid
;
15336 * Do not move objects in pg_catalog as part of this, if an admin
15337 * really wishes to do so, they can issue the individual ALTER
15338 * commands directly.
15340 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15341 * (TOAST will be moved with the main table).
15343 if (IsCatalogNamespace(relForm
->relnamespace
) ||
15344 relForm
->relisshared
||
15345 isAnyTempNamespace(relForm
->relnamespace
) ||
15346 IsToastNamespace(relForm
->relnamespace
))
15349 /* Only move the object type requested */
15350 if ((stmt
->objtype
== OBJECT_TABLE
&&
15351 relForm
->relkind
!= RELKIND_RELATION
&&
15352 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
15353 (stmt
->objtype
== OBJECT_INDEX
&&
15354 relForm
->relkind
!= RELKIND_INDEX
&&
15355 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
15356 (stmt
->objtype
== OBJECT_MATVIEW
&&
15357 relForm
->relkind
!= RELKIND_MATVIEW
))
15360 /* Check if we are only moving objects owned by certain roles */
15361 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
15365 * Handle permissions-checking here since we are locking the tables
15366 * and also to avoid doing a bunch of work only to fail part-way. Note
15367 * that permissions will also be checked by AlterTableInternal().
15369 * Caller must be considered an owner on the table to move it.
15371 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
15372 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
15373 NameStr(relForm
->relname
));
15375 if (stmt
->nowait
&&
15376 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
15378 (errcode(ERRCODE_OBJECT_IN_USE
),
15379 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15380 get_namespace_name(relForm
->relnamespace
),
15381 NameStr(relForm
->relname
))));
15383 LockRelationOid(relOid
, AccessExclusiveLock
);
15385 /* Add to our list of objects to move */
15386 relations
= lappend_oid(relations
, relOid
);
15389 table_endscan(scan
);
15390 table_close(rel
, AccessShareLock
);
15392 if (relations
== NIL
)
15394 (errcode(ERRCODE_NO_DATA_FOUND
),
15395 errmsg("no matching relations in tablespace \"%s\" found",
15396 orig_tablespaceoid
== InvalidOid
? "(database default)" :
15397 get_tablespace_name(orig_tablespaceoid
))));
15399 /* Everything is locked, loop through and move all of the relations. */
15400 foreach(l
, relations
)
15403 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
15405 cmd
->subtype
= AT_SetTableSpace
;
15406 cmd
->name
= stmt
->new_tablespacename
;
15408 cmds
= lappend(cmds
, cmd
);
15410 EventTriggerAlterTableStart((Node
*) stmt
);
15411 /* OID is set by AlterTableInternal */
15412 AlterTableInternal(lfirst_oid(l
), cmds
, false);
15413 EventTriggerAlterTableEnd();
15416 return new_tablespaceoid
;
15420 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
15422 SMgrRelation dstrel
;
15424 dstrel
= smgropen(newrlocator
, rel
->rd_backend
);
15427 * Since we copy the file directly without looking at the shared buffers,
15428 * we'd better first flush out any pages of the source relation that are
15429 * in shared buffers. We assume no new changes will be made while we are
15430 * holding exclusive lock on the rel.
15432 FlushRelationBuffers(rel
);
15435 * Create and copy all forks of the relation, and schedule unlinking of
15436 * old physical files.
15438 * NOTE: any conflict in relfilenumber value will be caught in
15439 * RelationCreateStorage().
15441 RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
15443 /* copy main fork */
15444 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
15445 rel
->rd_rel
->relpersistence
);
15447 /* copy those extra forks that exist */
15448 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
15449 forkNum
<= MAX_FORKNUM
; forkNum
++)
15451 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
15453 smgrcreate(dstrel
, forkNum
, false);
15456 * WAL log creation if the relation is persistent, or this is the
15457 * init fork of an unlogged relation.
15459 if (RelationIsPermanent(rel
) ||
15460 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
15461 forkNum
== INIT_FORKNUM
))
15462 log_smgrcreate(&newrlocator
, forkNum
);
15463 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
15464 rel
->rd_rel
->relpersistence
);
15468 /* drop old relation, and close new one */
15469 RelationDropStorage(rel
);
15474 * ALTER TABLE ENABLE/DISABLE TRIGGER
15476 * We just pass this off to trigger.c.
15479 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
15480 char fires_when
, bool skip_system
, bool recurse
,
15483 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
15484 fires_when
, skip_system
, recurse
,
15487 InvokeObjectPostAlterHook(RelationRelationId
,
15488 RelationGetRelid(rel
), 0);
15492 * ALTER TABLE ENABLE/DISABLE RULE
15494 * We just pass this off to rewriteDefine.c.
15497 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
15498 char fires_when
, LOCKMODE lockmode
)
15500 EnableDisableRule(rel
, rulename
, fires_when
);
15502 InvokeObjectPostAlterHook(RelationRelationId
,
15503 RelationGetRelid(rel
), 0);
15507 * ALTER TABLE INHERIT
15509 * Add a parent to the child's parents. This verifies that all the columns and
15510 * check constraints of the parent appear in the child and that they have the
15511 * same data types and expressions.
15514 ATPrepAddInherit(Relation child_rel
)
15516 if (child_rel
->rd_rel
->reloftype
)
15518 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15519 errmsg("cannot change inheritance of typed table")));
15521 if (child_rel
->rd_rel
->relispartition
)
15523 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15524 errmsg("cannot change inheritance of a partition")));
15526 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15528 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15529 errmsg("cannot change inheritance of partitioned table")));
15533 * Return the address of the new parent relation.
15535 static ObjectAddress
15536 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
15538 Relation parent_rel
;
15540 ObjectAddress address
;
15541 const char *trigger_name
;
15544 * A self-exclusive lock is needed here. See the similar case in
15545 * MergeAttributes() for a full explanation.
15547 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
15550 * Must be owner of both parent and child -- child was checked by
15551 * ATSimplePermissions call in ATPrepCmd
15553 ATSimplePermissions(AT_AddInherit
, parent_rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
15555 /* Permanent rels cannot inherit from temporary ones */
15556 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15557 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
15559 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15560 errmsg("cannot inherit from temporary relation \"%s\"",
15561 RelationGetRelationName(parent_rel
))));
15563 /* If parent rel is temp, it must belong to this session */
15564 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15565 !parent_rel
->rd_islocaltemp
)
15567 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15568 errmsg("cannot inherit from temporary relation of another session")));
15570 /* Ditto for the child */
15571 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15572 !child_rel
->rd_islocaltemp
)
15574 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15575 errmsg("cannot inherit to temporary relation of another session")));
15577 /* Prevent partitioned tables from becoming inheritance parents */
15578 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15580 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15581 errmsg("cannot inherit from partitioned table \"%s\"",
15582 parent
->relname
)));
15584 /* Likewise for partitions */
15585 if (parent_rel
->rd_rel
->relispartition
)
15587 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15588 errmsg("cannot inherit from a partition")));
15591 * Prevent circularity by seeing if proposed parent inherits from child.
15592 * (In particular, this disallows making a rel inherit from itself.)
15594 * This is not completely bulletproof because of race conditions: in
15595 * multi-level inheritance trees, someone else could concurrently be
15596 * making another inheritance link that closes the loop but does not join
15597 * either of the rels we have locked. Preventing that seems to require
15598 * exclusive locks on the entire inheritance tree, which is a cure worse
15599 * than the disease. find_all_inheritors() will cope with circularity
15600 * anyway, so don't sweat it too much.
15602 * We use weakest lock we can on child's children, namely AccessShareLock.
15604 children
= find_all_inheritors(RelationGetRelid(child_rel
),
15605 AccessShareLock
, NULL
);
15607 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
15609 (errcode(ERRCODE_DUPLICATE_TABLE
),
15610 errmsg("circular inheritance not allowed"),
15611 errdetail("\"%s\" is already a child of \"%s\".",
15613 RelationGetRelationName(child_rel
))));
15616 * If child_rel has row-level triggers with transition tables, we
15617 * currently don't allow it to become an inheritance child. See also
15618 * prohibitions in ATExecAttachPartition() and CreateTrigger().
15620 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
15621 if (trigger_name
!= NULL
)
15623 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15624 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15625 trigger_name
, RelationGetRelationName(child_rel
)),
15626 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15628 /* OK to create inheritance */
15629 CreateInheritance(child_rel
, parent_rel
);
15632 * If parent_rel has a primary key, then child_rel has not-null
15633 * constraints that make these columns as non nullable. Make those
15634 * constraints as inherited.
15636 ATInheritAdjustNotNulls(parent_rel
, child_rel
, 1);
15638 ObjectAddressSet(address
, RelationRelationId
,
15639 RelationGetRelid(parent_rel
));
15641 /* keep our lock on the parent relation until commit */
15642 table_close(parent_rel
, NoLock
);
15648 * CreateInheritance
15649 * Catalog manipulation portion of creating inheritance between a child
15650 * table and a parent table.
15652 * Common to ATExecAddInherit() and ATExecAttachPartition().
15655 CreateInheritance(Relation child_rel
, Relation parent_rel
)
15657 Relation catalogRelation
;
15660 HeapTuple inheritsTuple
;
15663 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15664 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
15667 * Check for duplicates in the list of parents, and determine the highest
15668 * inhseqno already present; we'll use the next one for the new parent.
15669 * Also, if proposed child is a partition, it cannot already be
15672 * Note: we do not reject the case where the child already inherits from
15673 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15676 Anum_pg_inherits_inhrelid
,
15677 BTEqualStrategyNumber
, F_OIDEQ
,
15678 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15679 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
15680 true, NULL
, 1, &key
);
15682 /* inhseqno sequences start at 1 */
15684 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
15686 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
15688 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
15690 (errcode(ERRCODE_DUPLICATE_TABLE
),
15691 errmsg("relation \"%s\" would be inherited from more than once",
15692 RelationGetRelationName(parent_rel
))));
15694 if (inh
->inhseqno
> inhseqno
)
15695 inhseqno
= inh
->inhseqno
;
15697 systable_endscan(scan
);
15699 /* Match up the columns and bump attinhcount as needed */
15700 MergeAttributesIntoExisting(child_rel
, parent_rel
);
15702 /* Match up the constraints and bump coninhcount as needed */
15703 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
15706 * OK, it looks valid. Make the catalog entries that show inheritance.
15708 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
15709 RelationGetRelid(parent_rel
),
15712 parent_rel
->rd_rel
->relkind
==
15713 RELKIND_PARTITIONED_TABLE
);
15715 /* Now we're done with pg_inherits */
15716 table_close(catalogRelation
, RowExclusiveLock
);
15720 * Obtain the source-text form of the constraint expression for a check
15721 * constraint, given its pg_constraint tuple
15724 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
15726 Form_pg_constraint con
;
15731 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
15732 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
15734 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
15736 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
15737 ObjectIdGetDatum(con
->conrelid
));
15738 return TextDatumGetCString(expr
);
15742 * Determine whether two check constraints are functionally equivalent
15744 * The test we apply is to see whether they reverse-compile to the same
15745 * source string. This insulates us from issues like whether attributes
15746 * have the same physical column numbers in parent and child relations.
15749 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
15751 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
15752 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
15754 if (acon
->condeferrable
!= bcon
->condeferrable
||
15755 acon
->condeferred
!= bcon
->condeferred
||
15756 strcmp(decompile_conbin(a
, tupleDesc
),
15757 decompile_conbin(b
, tupleDesc
)) != 0)
15764 * Check columns in child table match up with columns in parent, and increment
15765 * their attinhcount.
15767 * Called by CreateInheritance
15769 * Currently all parent columns must be found in child. Missing columns are an
15770 * error. One day we might consider creating new columns like CREATE TABLE
15771 * does. However, that is widely unpopular --- in the common use case of
15772 * partitioned tables it's a foot-gun.
15774 * The data type must match exactly. If the parent column is NOT NULL then
15775 * the child must be as well. Defaults are not compared, however.
15778 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
)
15781 TupleDesc parent_desc
;
15783 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
15784 parent_desc
= RelationGetDescr(parent_rel
);
15786 for (AttrNumber parent_attno
= 1; parent_attno
<= parent_desc
->natts
; parent_attno
++)
15788 Form_pg_attribute parent_att
= TupleDescAttr(parent_desc
, parent_attno
- 1);
15789 char *parent_attname
= NameStr(parent_att
->attname
);
15792 /* Ignore dropped columns in the parent. */
15793 if (parent_att
->attisdropped
)
15796 /* Find same column in child (matching on column name). */
15797 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
), parent_attname
);
15798 if (HeapTupleIsValid(tuple
))
15800 Form_pg_attribute child_att
= (Form_pg_attribute
) GETSTRUCT(tuple
);
15802 if (parent_att
->atttypid
!= child_att
->atttypid
||
15803 parent_att
->atttypmod
!= child_att
->atttypmod
)
15805 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15806 errmsg("child table \"%s\" has different type for column \"%s\"",
15807 RelationGetRelationName(child_rel
), parent_attname
)));
15809 if (parent_att
->attcollation
!= child_att
->attcollation
)
15811 (errcode(ERRCODE_COLLATION_MISMATCH
),
15812 errmsg("child table \"%s\" has different collation for column \"%s\"",
15813 RelationGetRelationName(child_rel
), parent_attname
)));
15816 * If the parent has a not-null constraint that's not NO INHERIT,
15817 * make sure the child has one too.
15819 * Other constraints are checked elsewhere.
15821 if (parent_att
->attnotnull
&& !child_att
->attnotnull
)
15825 contup
= findNotNullConstraintAttnum(RelationGetRelid(parent_rel
),
15826 parent_att
->attnum
);
15827 if (HeapTupleIsValid(contup
) &&
15828 !((Form_pg_constraint
) GETSTRUCT(contup
))->connoinherit
)
15830 errcode(ERRCODE_DATATYPE_MISMATCH
),
15831 errmsg("column \"%s\" in child table must be marked NOT NULL",
15836 * Child column must be generated if and only if parent column is.
15838 if (parent_att
->attgenerated
&& !child_att
->attgenerated
)
15840 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15841 errmsg("column \"%s\" in child table must be a generated column", parent_attname
)));
15842 if (child_att
->attgenerated
&& !parent_att
->attgenerated
)
15844 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15845 errmsg("column \"%s\" in child table must not be a generated column", parent_attname
)));
15848 * OK, bump the child column's inheritance count. (If we fail
15849 * later on, this change will just roll back.)
15851 child_att
->attinhcount
++;
15852 if (child_att
->attinhcount
< 0)
15854 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
15855 errmsg("too many inheritance parents"));
15858 * In case of partitions, we must enforce that value of attislocal
15859 * is same in all partitions. (Note: there are only inherited
15860 * attributes in partitions)
15862 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15864 Assert(child_att
->attinhcount
== 1);
15865 child_att
->attislocal
= false;
15868 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
15869 heap_freetuple(tuple
);
15874 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15875 errmsg("child table is missing column \"%s\"", parent_attname
)));
15879 table_close(attrrel
, RowExclusiveLock
);
15883 * Check constraints in child table match up with constraints in parent,
15884 * and increment their coninhcount.
15886 * Constraints that are marked ONLY in the parent are ignored.
15888 * Called by CreateInheritance
15890 * Currently all constraints in parent must be present in the child. One day we
15891 * may consider adding new constraints like CREATE TABLE does.
15893 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15894 * constraints. As long as tables have more like 10 constraints it shouldn't be
15895 * a problem though. Even 100 constraints ought not be the end of the world.
15897 * XXX See MergeWithExistingConstraint too if you change this code.
15900 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
15902 Relation constraintrel
;
15903 SysScanDesc parent_scan
;
15904 ScanKeyData parent_key
;
15905 HeapTuple parent_tuple
;
15906 Oid parent_relid
= RelationGetRelid(parent_rel
);
15908 constraintrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
15910 /* Outer loop scans through the parent's constraint definitions */
15911 ScanKeyInit(&parent_key
,
15912 Anum_pg_constraint_conrelid
,
15913 BTEqualStrategyNumber
, F_OIDEQ
,
15914 ObjectIdGetDatum(parent_relid
));
15915 parent_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
15916 true, NULL
, 1, &parent_key
);
15918 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
15920 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
15921 SysScanDesc child_scan
;
15922 ScanKeyData child_key
;
15923 HeapTuple child_tuple
;
15924 bool found
= false;
15926 if (parent_con
->contype
!= CONSTRAINT_CHECK
&&
15927 parent_con
->contype
!= CONSTRAINT_NOTNULL
)
15930 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15931 if (parent_con
->connoinherit
)
15934 /* Search for a child constraint matching this one */
15935 ScanKeyInit(&child_key
,
15936 Anum_pg_constraint_conrelid
,
15937 BTEqualStrategyNumber
, F_OIDEQ
,
15938 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15939 child_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
15940 true, NULL
, 1, &child_key
);
15942 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
15944 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
15945 HeapTuple child_copy
;
15947 if (child_con
->contype
!= parent_con
->contype
)
15951 * CHECK constraint are matched by name, NOT NULL ones by
15954 if (child_con
->contype
== CONSTRAINT_CHECK
)
15956 if (strcmp(NameStr(parent_con
->conname
),
15957 NameStr(child_con
->conname
)) != 0)
15960 else if (child_con
->contype
== CONSTRAINT_NOTNULL
)
15962 AttrNumber parent_attno
= extractNotNullColumn(parent_tuple
);
15963 AttrNumber child_attno
= extractNotNullColumn(child_tuple
);
15965 if (strcmp(get_attname(parent_relid
, parent_attno
, false),
15966 get_attname(RelationGetRelid(child_rel
), child_attno
,
15971 if (child_con
->contype
== CONSTRAINT_CHECK
&&
15972 !constraints_equivalent(parent_tuple
, child_tuple
, RelationGetDescr(constraintrel
)))
15974 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15975 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15976 RelationGetRelationName(child_rel
), NameStr(parent_con
->conname
))));
15979 * If the CHECK child constraint is "no inherit" then cannot
15982 * This is not desirable for not-null constraints, mostly because
15983 * it breaks our pg_upgrade strategy, but it also makes sense on
15984 * its own: if a child has its own not-null constraint and then
15985 * acquires a parent with the same constraint, then we start to
15986 * enforce that constraint for all the descendants of that child
15989 if (child_con
->contype
== CONSTRAINT_CHECK
&&
15990 child_con
->connoinherit
)
15992 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
15993 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15994 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
15997 * If the child constraint is "not valid" then cannot merge with a
15998 * valid parent constraint
16000 if (parent_con
->convalidated
&& !child_con
->convalidated
)
16002 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16003 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16004 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16007 * OK, bump the child constraint's inheritance count. (If we fail
16008 * later on, this change will just roll back.)
16010 child_copy
= heap_copytuple(child_tuple
);
16011 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
16012 child_con
->coninhcount
++;
16013 if (child_con
->coninhcount
< 0)
16015 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16016 errmsg("too many inheritance parents"));
16017 if (child_con
->contype
== CONSTRAINT_NOTNULL
&&
16018 child_con
->connoinherit
)
16019 child_con
->connoinherit
= false;
16022 * In case of partitions, an inherited constraint must be
16023 * inherited only once since it cannot have multiple parents and
16024 * it is never considered local.
16026 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16028 Assert(child_con
->coninhcount
== 1);
16029 child_con
->conislocal
= false;
16032 CatalogTupleUpdate(constraintrel
, &child_copy
->t_self
, child_copy
);
16033 heap_freetuple(child_copy
);
16039 systable_endscan(child_scan
);
16043 if (parent_con
->contype
== CONSTRAINT_NOTNULL
)
16045 errcode(ERRCODE_DATATYPE_MISMATCH
),
16046 errmsg("column \"%s\" in child table must be marked NOT NULL",
16047 get_attname(parent_relid
,
16048 extractNotNullColumn(parent_tuple
),
16052 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16053 errmsg("child table is missing constraint \"%s\"",
16054 NameStr(parent_con
->conname
))));
16058 systable_endscan(parent_scan
);
16059 table_close(constraintrel
, RowExclusiveLock
);
16063 * ALTER TABLE NO INHERIT
16065 * Return value is the address of the relation that is no longer parent.
16067 static ObjectAddress
16068 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16070 ObjectAddress address
;
16071 Relation parent_rel
;
16073 if (rel
->rd_rel
->relispartition
)
16075 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16076 errmsg("cannot change inheritance of a partition")));
16079 * AccessShareLock on the parent is probably enough, seeing that DROP
16080 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16081 * be inspecting the parent's schema.
16083 parent_rel
= table_openrv(parent
, AccessShareLock
);
16086 * We don't bother to check ownership of the parent table --- ownership of
16087 * the child is presumed enough rights.
16090 /* Off to RemoveInheritance() where most of the work happens */
16091 RemoveInheritance(rel
, parent_rel
, false);
16094 * If parent_rel has a primary key, then child_rel has not-null
16095 * constraints that make these columns as non nullable. Mark those
16096 * constraints as no longer inherited by this parent.
16098 ATInheritAdjustNotNulls(parent_rel
, rel
, -1);
16101 * If the parent has a primary key, then we decrement counts for all NOT
16105 ObjectAddressSet(address
, RelationRelationId
,
16106 RelationGetRelid(parent_rel
));
16108 /* keep our lock on the parent relation until commit */
16109 table_close(parent_rel
, NoLock
);
16115 * MarkInheritDetached
16117 * Set inhdetachpending for a partition, for ATExecDetachPartition
16118 * in concurrent mode. While at it, verify that no other partition is
16119 * already pending detach.
16122 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
16124 Relation catalogRelation
;
16127 HeapTuple inheritsTuple
;
16128 bool found
= false;
16130 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16133 * Find pg_inherits entries by inhparent. (We need to scan them all in
16134 * order to verify that no other partition is pending detach.)
16136 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16138 Anum_pg_inherits_inhparent
,
16139 BTEqualStrategyNumber
, F_OIDEQ
,
16140 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16141 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
16142 true, NULL
, 1, &key
);
16144 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16146 Form_pg_inherits inhForm
;
16148 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16149 if (inhForm
->inhdetachpending
)
16151 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16152 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16153 get_rel_name(inhForm
->inhrelid
),
16154 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
16155 RelationGetRelationName(parent_rel
)),
16156 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16158 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
16162 newtup
= heap_copytuple(inheritsTuple
);
16163 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
16165 CatalogTupleUpdate(catalogRelation
,
16166 &inheritsTuple
->t_self
,
16169 heap_freetuple(newtup
);
16170 /* keep looking, to ensure we catch others pending detach */
16175 systable_endscan(scan
);
16176 table_close(catalogRelation
, RowExclusiveLock
);
16180 (errcode(ERRCODE_UNDEFINED_TABLE
),
16181 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16182 RelationGetRelationName(child_rel
),
16183 RelationGetRelationName(parent_rel
))));
16187 * RemoveInheritance
16189 * Drop a parent from the child's parents. This just adjusts the attinhcount
16190 * and attislocal of the columns and removes the pg_inherit and pg_depend
16191 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16193 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16194 * up attislocal stays true, which means if a child is ever removed from a
16195 * parent then its columns will never be automatically dropped which may
16196 * surprise. But at least we'll never surprise by dropping columns someone
16197 * isn't expecting to be dropped which would actually mean data loss.
16199 * coninhcount and conislocal for inherited constraints are adjusted in
16200 * exactly the same way.
16202 * Common to ATExecDropInherit() and ATExecDetachPartition().
16205 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
16207 Relation catalogRelation
;
16209 ScanKeyData key
[3];
16210 HeapTuple attributeTuple
,
16215 bool is_partitioning
;
16217 is_partitioning
= (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16219 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
16220 RelationGetRelid(parent_rel
),
16222 RelationGetRelationName(child_rel
));
16225 if (is_partitioning
)
16227 (errcode(ERRCODE_UNDEFINED_TABLE
),
16228 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16229 RelationGetRelationName(child_rel
),
16230 RelationGetRelationName(parent_rel
))));
16233 (errcode(ERRCODE_UNDEFINED_TABLE
),
16234 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16235 RelationGetRelationName(parent_rel
),
16236 RelationGetRelationName(child_rel
))));
16240 * Search through child columns looking for ones matching parent rel
16242 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
16243 ScanKeyInit(&key
[0],
16244 Anum_pg_attribute_attrelid
,
16245 BTEqualStrategyNumber
, F_OIDEQ
,
16246 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16247 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
16248 true, NULL
, 1, key
);
16249 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
16251 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
16253 /* Ignore if dropped or not inherited */
16254 if (att
->attisdropped
)
16256 if (att
->attinhcount
<= 0)
16259 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
16260 NameStr(att
->attname
)))
16262 /* Decrement inhcount and possibly set islocal to true */
16263 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
16264 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
16266 copy_att
->attinhcount
--;
16267 if (copy_att
->attinhcount
== 0)
16268 copy_att
->attislocal
= true;
16270 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16271 heap_freetuple(copyTuple
);
16274 systable_endscan(scan
);
16275 table_close(catalogRelation
, RowExclusiveLock
);
16278 * Likewise, find inherited check constraints and disinherit them. To do
16279 * this, we first need a list of the names of the parent's check
16280 * constraints. (We cheat a bit by only checking for name matches,
16281 * assuming that the expressions will match.)
16283 * For NOT NULL columns, we store column numbers to match.
16285 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16286 ScanKeyInit(&key
[0],
16287 Anum_pg_constraint_conrelid
,
16288 BTEqualStrategyNumber
, F_OIDEQ
,
16289 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16290 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16291 true, NULL
, 1, key
);
16296 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16298 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16300 if (con
->contype
== CONSTRAINT_CHECK
)
16301 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
16302 if (con
->contype
== CONSTRAINT_NOTNULL
)
16303 nncolumns
= lappend_int(nncolumns
, extractNotNullColumn(constraintTuple
));
16306 systable_endscan(scan
);
16308 /* Now scan the child's constraints */
16309 ScanKeyInit(&key
[0],
16310 Anum_pg_constraint_conrelid
,
16311 BTEqualStrategyNumber
, F_OIDEQ
,
16312 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16313 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16314 true, NULL
, 1, key
);
16316 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16318 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16319 bool match
= false;
16323 * Match CHECK constraints by name, not-null constraints by column
16324 * number, and ignore all others.
16326 if (con
->contype
== CONSTRAINT_CHECK
)
16328 foreach(lc
, connames
)
16330 if (con
->contype
== CONSTRAINT_CHECK
&&
16331 strcmp(NameStr(con
->conname
), (char *) lfirst(lc
)) == 0)
16338 else if (con
->contype
== CONSTRAINT_NOTNULL
)
16340 AttrNumber child_attno
= extractNotNullColumn(constraintTuple
);
16342 foreach(lc
, nncolumns
)
16344 if (lfirst_int(lc
) == child_attno
)
16356 /* Decrement inhcount and possibly set islocal to true */
16357 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
16358 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
16360 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
16361 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
16362 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
16364 copy_con
->coninhcount
--;
16365 if (copy_con
->coninhcount
== 0)
16366 copy_con
->conislocal
= true;
16368 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16369 heap_freetuple(copyTuple
);
16373 systable_endscan(scan
);
16374 table_close(catalogRelation
, RowExclusiveLock
);
16376 drop_parent_dependency(RelationGetRelid(child_rel
),
16377 RelationRelationId
,
16378 RelationGetRelid(parent_rel
),
16379 child_dependency_type(is_partitioning
));
16382 * Post alter hook of this inherits. Since object_access_hook doesn't take
16383 * multiple object identifiers, we relay oid of parent relation using
16384 * auxiliary_id argument.
16386 InvokeObjectPostAlterHookArg(InheritsRelationId
,
16387 RelationGetRelid(child_rel
), 0,
16388 RelationGetRelid(parent_rel
), false);
16392 * Adjust coninhcount of not-null constraints upwards or downwards when a
16393 * table is marked as inheriting or no longer doing so a table with a primary
16396 * Note: these constraints are not dropped, even if their inhcount goes to zero
16397 * and conislocal is false. Instead we mark the constraints as locally defined.
16398 * This is seen as more useful behavior, with no downsides. The user can always
16399 * drop them afterwards.
16402 ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
, int inhcount
)
16404 Bitmapset
*pkattnos
;
16406 /* Quick exit when parent has no PK */
16407 if (!parent_rel
->rd_rel
->relhasindex
)
16410 pkattnos
= RelationGetIndexAttrBitmap(parent_rel
,
16411 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
16412 if (pkattnos
!= NULL
)
16414 Bitmapset
*childattnums
= NULL
;
16418 attmap
= build_attrmap_by_name(RelationGetDescr(parent_rel
),
16419 RelationGetDescr(child_rel
), true);
16422 while ((i
= bms_next_member(pkattnos
, i
)) >= 0)
16424 childattnums
= bms_add_member(childattnums
,
16425 attmap
->attnums
[i
+ FirstLowInvalidHeapAttributeNumber
- 1]);
16429 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
16430 * parent: the relevant not-null constraint in the child already had
16431 * its inhcount modified earlier.
16433 CommandCounterIncrement();
16434 AdjustNotNullInheritance(RelationGetRelid(child_rel
), childattnums
,
16440 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16441 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16442 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16443 * be TypeRelationId). There's no convenient way to do this, so go trawling
16444 * through pg_depend.
16447 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
16448 DependencyType deptype
)
16450 Relation catalogRelation
;
16452 ScanKeyData key
[3];
16453 HeapTuple depTuple
;
16455 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
16457 ScanKeyInit(&key
[0],
16458 Anum_pg_depend_classid
,
16459 BTEqualStrategyNumber
, F_OIDEQ
,
16460 ObjectIdGetDatum(RelationRelationId
));
16461 ScanKeyInit(&key
[1],
16462 Anum_pg_depend_objid
,
16463 BTEqualStrategyNumber
, F_OIDEQ
,
16464 ObjectIdGetDatum(relid
));
16465 ScanKeyInit(&key
[2],
16466 Anum_pg_depend_objsubid
,
16467 BTEqualStrategyNumber
, F_INT4EQ
,
16470 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
16473 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
16475 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
16477 if (dep
->refclassid
== refclassid
&&
16478 dep
->refobjid
== refobjid
&&
16479 dep
->refobjsubid
== 0 &&
16480 dep
->deptype
== deptype
)
16481 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
16484 systable_endscan(scan
);
16485 table_close(catalogRelation
, RowExclusiveLock
);
16491 * Attach a table to a composite type, as though it had been created with CREATE
16492 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16493 * subject table must not have inheritance parents. These restrictions ensure
16494 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16496 * The address of the type is returned.
16498 static ObjectAddress
16499 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
16501 Oid relid
= RelationGetRelid(rel
);
16503 Form_pg_type typeform
;
16505 Relation inheritsRelation
,
16509 AttrNumber table_attno
,
16511 TupleDesc typeTupleDesc
,
16513 ObjectAddress tableobj
,
16515 HeapTuple classtuple
;
16517 /* Validate the type. */
16518 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
16519 check_of_type(typetuple
);
16520 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
16521 typeid = typeform
->oid
;
16523 /* Fail if the table has any inheritance parents. */
16524 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
16526 Anum_pg_inherits_inhrelid
,
16527 BTEqualStrategyNumber
, F_OIDEQ
,
16528 ObjectIdGetDatum(relid
));
16529 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
16530 true, NULL
, 1, &key
);
16531 if (HeapTupleIsValid(systable_getnext(scan
)))
16533 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16534 errmsg("typed tables cannot inherit")));
16535 systable_endscan(scan
);
16536 table_close(inheritsRelation
, AccessShareLock
);
16539 * Check the tuple descriptors for compatibility. Unlike inheritance, we
16540 * require that the order also match. However, attnotnull need not match.
16542 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
16543 tableTupleDesc
= RelationGetDescr(rel
);
16545 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
16547 Form_pg_attribute type_attr
,
16549 const char *type_attname
,
16552 /* Get the next non-dropped type attribute. */
16553 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
16554 if (type_attr
->attisdropped
)
16556 type_attname
= NameStr(type_attr
->attname
);
16558 /* Get the next non-dropped table attribute. */
16561 if (table_attno
> tableTupleDesc
->natts
)
16563 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16564 errmsg("table is missing column \"%s\"",
16566 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
16568 } while (table_attr
->attisdropped
);
16569 table_attname
= NameStr(table_attr
->attname
);
16571 /* Compare name. */
16572 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
16574 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16575 errmsg("table has column \"%s\" where type requires \"%s\"",
16576 table_attname
, type_attname
)));
16578 /* Compare type. */
16579 if (table_attr
->atttypid
!= type_attr
->atttypid
||
16580 table_attr
->atttypmod
!= type_attr
->atttypmod
||
16581 table_attr
->attcollation
!= type_attr
->attcollation
)
16583 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16584 errmsg("table \"%s\" has different type for column \"%s\"",
16585 RelationGetRelationName(rel
), type_attname
)));
16587 ReleaseTupleDesc(typeTupleDesc
);
16589 /* Any remaining columns at the end of the table had better be dropped. */
16590 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
16592 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
16595 if (!table_attr
->attisdropped
)
16597 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16598 errmsg("table has extra column \"%s\"",
16599 NameStr(table_attr
->attname
))));
16602 /* If the table was already typed, drop the existing dependency. */
16603 if (rel
->rd_rel
->reloftype
)
16604 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
16605 DEPENDENCY_NORMAL
);
16607 /* Record a dependency on the new type. */
16608 tableobj
.classId
= RelationRelationId
;
16609 tableobj
.objectId
= relid
;
16610 tableobj
.objectSubId
= 0;
16611 typeobj
.classId
= TypeRelationId
;
16612 typeobj
.objectId
= typeid;
16613 typeobj
.objectSubId
= 0;
16614 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
16616 /* Update pg_class.reloftype */
16617 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
16618 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16619 if (!HeapTupleIsValid(classtuple
))
16620 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16621 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
16622 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
16624 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
16626 heap_freetuple(classtuple
);
16627 table_close(relationRelation
, RowExclusiveLock
);
16629 ReleaseSysCache(typetuple
);
16635 * ALTER TABLE NOT OF
16637 * Detach a typed table from its originating type. Just clear reloftype and
16638 * remove the dependency.
16641 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
16643 Oid relid
= RelationGetRelid(rel
);
16644 Relation relationRelation
;
16647 if (!OidIsValid(rel
->rd_rel
->reloftype
))
16649 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16650 errmsg("\"%s\" is not a typed table",
16651 RelationGetRelationName(rel
))));
16654 * We don't bother to check ownership of the type --- ownership of the
16655 * table is presumed enough rights. No lock required on the type, either.
16658 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
16659 DEPENDENCY_NORMAL
);
16661 /* Clear pg_class.reloftype */
16662 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
16663 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16664 if (!HeapTupleIsValid(tuple
))
16665 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16666 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
16667 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
16669 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
16671 heap_freetuple(tuple
);
16672 table_close(relationRelation
, RowExclusiveLock
);
16676 * relation_mark_replica_identity: Update a table's replica identity
16678 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16679 * index. Otherwise, it must be InvalidOid.
16681 * Caller had better hold an exclusive lock on the relation, as the results
16682 * of running two of these concurrently wouldn't be pretty.
16685 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
16690 HeapTuple pg_class_tuple
;
16691 HeapTuple pg_index_tuple
;
16692 Form_pg_class pg_class_form
;
16693 Form_pg_index pg_index_form
;
16697 * Check whether relreplident has changed, and update it if so.
16699 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16700 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
16701 ObjectIdGetDatum(RelationGetRelid(rel
)));
16702 if (!HeapTupleIsValid(pg_class_tuple
))
16703 elog(ERROR
, "cache lookup failed for relation \"%s\"",
16704 RelationGetRelationName(rel
));
16705 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
16706 if (pg_class_form
->relreplident
!= ri_type
)
16708 pg_class_form
->relreplident
= ri_type
;
16709 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
16711 table_close(pg_class
, RowExclusiveLock
);
16712 heap_freetuple(pg_class_tuple
);
16715 * Update the per-index indisreplident flags correctly.
16717 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
16718 foreach(index
, RelationGetIndexList(rel
))
16720 Oid thisIndexOid
= lfirst_oid(index
);
16721 bool dirty
= false;
16723 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
16724 ObjectIdGetDatum(thisIndexOid
));
16725 if (!HeapTupleIsValid(pg_index_tuple
))
16726 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
16727 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
16729 if (thisIndexOid
== indexOid
)
16731 /* Set the bit if not already set. */
16732 if (!pg_index_form
->indisreplident
)
16735 pg_index_form
->indisreplident
= true;
16740 /* Unset the bit if set. */
16741 if (pg_index_form
->indisreplident
)
16744 pg_index_form
->indisreplident
= false;
16750 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
16751 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
16752 InvalidOid
, is_internal
);
16755 * Invalidate the relcache for the table, so that after we commit
16756 * all sessions will refresh the table's replica identity index
16757 * before attempting any UPDATE or DELETE on the table. (If we
16758 * changed the table's pg_class row above, then a relcache inval
16759 * is already queued due to that; but we might not have.)
16761 CacheInvalidateRelcache(rel
);
16763 heap_freetuple(pg_index_tuple
);
16766 table_close(pg_index
, RowExclusiveLock
);
16770 * ALTER TABLE <name> REPLICA IDENTITY ...
16773 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
16779 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
16781 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16784 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
16786 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16789 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
16791 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16794 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
16796 /* fallthrough */ ;
16799 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
16801 /* Check that the index exists */
16802 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
16803 if (!OidIsValid(indexOid
))
16805 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16806 errmsg("index \"%s\" for table \"%s\" does not exist",
16807 stmt
->name
, RelationGetRelationName(rel
))));
16809 indexRel
= index_open(indexOid
, ShareLock
);
16811 /* Check that the index is on the relation we're altering. */
16812 if (indexRel
->rd_index
== NULL
||
16813 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
16815 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16816 errmsg("\"%s\" is not an index for table \"%s\"",
16817 RelationGetRelationName(indexRel
),
16818 RelationGetRelationName(rel
))));
16819 /* The AM must support uniqueness, and the index must in fact be unique. */
16820 if (!indexRel
->rd_indam
->amcanunique
||
16821 !indexRel
->rd_index
->indisunique
)
16823 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16824 errmsg("cannot use non-unique index \"%s\" as replica identity",
16825 RelationGetRelationName(indexRel
))));
16826 /* Deferred indexes are not guaranteed to be always unique. */
16827 if (!indexRel
->rd_index
->indimmediate
)
16829 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16830 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16831 RelationGetRelationName(indexRel
))));
16832 /* Expression indexes aren't supported. */
16833 if (RelationGetIndexExpressions(indexRel
) != NIL
)
16835 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16836 errmsg("cannot use expression index \"%s\" as replica identity",
16837 RelationGetRelationName(indexRel
))));
16838 /* Predicate indexes aren't supported. */
16839 if (RelationGetIndexPredicate(indexRel
) != NIL
)
16841 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16842 errmsg("cannot use partial index \"%s\" as replica identity",
16843 RelationGetRelationName(indexRel
))));
16845 /* Check index for nullable columns. */
16846 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
16848 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
16849 Form_pg_attribute attr
;
16852 * Reject any other system columns. (Going forward, we'll disallow
16853 * indexes containing such columns in the first place, but they might
16854 * exist in older branches.)
16858 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
16859 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16860 RelationGetRelationName(indexRel
), attno
)));
16862 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
16863 if (!attr
->attnotnull
)
16865 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16866 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16867 RelationGetRelationName(indexRel
),
16868 NameStr(attr
->attname
))));
16871 /* This index is suitable for use as a replica identity. Mark it. */
16872 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
16874 index_close(indexRel
, NoLock
);
16878 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16881 ATExecSetRowSecurity(Relation rel
, bool rls
)
16887 relid
= RelationGetRelid(rel
);
16889 /* Pull the record for this relation and update it */
16890 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16892 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16894 if (!HeapTupleIsValid(tuple
))
16895 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16897 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
16898 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16900 InvokeObjectPostAlterHook(RelationRelationId
,
16901 RelationGetRelid(rel
), 0);
16903 table_close(pg_class
, RowExclusiveLock
);
16904 heap_freetuple(tuple
);
16908 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16911 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
16917 relid
= RelationGetRelid(rel
);
16919 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16921 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16923 if (!HeapTupleIsValid(tuple
))
16924 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16926 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
16927 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16929 InvokeObjectPostAlterHook(RelationRelationId
,
16930 RelationGetRelid(rel
), 0);
16932 table_close(pg_class
, RowExclusiveLock
);
16933 heap_freetuple(tuple
);
16937 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16940 ATExecGenericOptions(Relation rel
, List
*options
)
16943 ForeignServer
*server
;
16944 ForeignDataWrapper
*fdw
;
16947 Datum repl_val
[Natts_pg_foreign_table
];
16948 bool repl_null
[Natts_pg_foreign_table
];
16949 bool repl_repl
[Natts_pg_foreign_table
];
16951 Form_pg_foreign_table tableform
;
16953 if (options
== NIL
)
16956 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
16958 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
16959 ObjectIdGetDatum(rel
->rd_id
));
16960 if (!HeapTupleIsValid(tuple
))
16962 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16963 errmsg("foreign table \"%s\" does not exist",
16964 RelationGetRelationName(rel
))));
16965 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
16966 server
= GetForeignServer(tableform
->ftserver
);
16967 fdw
= GetForeignDataWrapper(server
->fdwid
);
16969 memset(repl_val
, 0, sizeof(repl_val
));
16970 memset(repl_null
, false, sizeof(repl_null
));
16971 memset(repl_repl
, false, sizeof(repl_repl
));
16973 /* Extract the current options */
16974 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
16976 Anum_pg_foreign_table_ftoptions
,
16979 datum
= PointerGetDatum(NULL
);
16981 /* Transform the options */
16982 datum
= transformGenericOptions(ForeignTableRelationId
,
16985 fdw
->fdwvalidator
);
16987 if (PointerIsValid(DatumGetPointer(datum
)))
16988 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
16990 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16992 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16994 /* Everything looks good - update the tuple */
16996 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
16997 repl_val
, repl_null
, repl_repl
);
16999 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
17002 * Invalidate relcache so that all sessions will refresh any cached plans
17003 * that might depend on the old options.
17005 CacheInvalidateRelcache(rel
);
17007 InvokeObjectPostAlterHook(ForeignTableRelationId
,
17008 RelationGetRelid(rel
), 0);
17010 table_close(ftrel
, RowExclusiveLock
);
17012 heap_freetuple(tuple
);
17016 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17018 * Return value is the address of the modified column
17020 static ObjectAddress
17021 ATExecSetCompression(Relation rel
,
17022 const char *column
,
17028 Form_pg_attribute atttableform
;
17032 ObjectAddress address
;
17034 compression
= strVal(newValue
);
17036 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
17038 /* copy the cache entry so we can scribble on it below */
17039 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
17040 if (!HeapTupleIsValid(tuple
))
17042 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17043 errmsg("column \"%s\" of relation \"%s\" does not exist",
17044 column
, RelationGetRelationName(rel
))));
17046 /* prevent them from altering a system attribute */
17047 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
17048 attnum
= atttableform
->attnum
;
17051 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17052 errmsg("cannot alter system column \"%s\"", column
)));
17055 * Check that column type is compressible, then get the attribute
17056 * compression method code
17058 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
17060 /* update pg_attribute entry */
17061 atttableform
->attcompression
= cmethod
;
17062 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
17064 InvokeObjectPostAlterHook(RelationRelationId
,
17065 RelationGetRelid(rel
),
17069 * Apply the change to indexes as well (only for simple index columns,
17070 * matching behavior of index.c ConstructTupleDescriptor()).
17072 SetIndexStorageProperties(rel
, attrel
, attnum
,
17077 heap_freetuple(tuple
);
17079 table_close(attrel
, RowExclusiveLock
);
17081 /* make changes visible */
17082 CommandCounterIncrement();
17084 ObjectAddressSubSet(address
, RelationRelationId
,
17085 RelationGetRelid(rel
), attnum
);
17091 * Preparation phase for SET LOGGED/UNLOGGED
17093 * This verifies that we're not trying to change a temp table. Also,
17094 * existing foreign key constraints are checked to avoid ending up with
17095 * permanent tables referencing unlogged tables.
17097 * Return value is false if the operation is a no-op (in which case the
17098 * checks are skipped), otherwise true.
17101 ATPrepChangePersistence(Relation rel
, bool toLogged
)
17103 Relation pg_constraint
;
17106 ScanKeyData skey
[1];
17109 * Disallow changing status for a temp table. Also verify whether we can
17110 * get away with doing nothing; in such cases we don't need to run the
17111 * checks below, either.
17113 switch (rel
->rd_rel
->relpersistence
)
17115 case RELPERSISTENCE_TEMP
:
17117 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17118 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17119 RelationGetRelationName(rel
)),
17122 case RELPERSISTENCE_PERMANENT
:
17124 /* nothing to do */
17127 case RELPERSISTENCE_UNLOGGED
:
17129 /* nothing to do */
17135 * Check that the table is not part of any publication when changing to
17136 * UNLOGGED, as UNLOGGED tables can't be published.
17139 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
17141 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
17142 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17143 RelationGetRelationName(rel
)),
17144 errdetail("Unlogged relations cannot be replicated.")));
17147 * Check existing foreign key constraints to preserve the invariant that
17148 * permanent tables cannot reference unlogged ones. Self-referencing
17149 * foreign keys can safely be ignored.
17151 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
17154 * Scan conrelid if changing to permanent, else confrelid. This also
17155 * determines whether a useful index exists.
17157 ScanKeyInit(&skey
[0],
17158 toLogged
? Anum_pg_constraint_conrelid
:
17159 Anum_pg_constraint_confrelid
,
17160 BTEqualStrategyNumber
, F_OIDEQ
,
17161 ObjectIdGetDatum(RelationGetRelid(rel
)));
17162 scan
= systable_beginscan(pg_constraint
,
17163 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
17164 true, NULL
, 1, skey
);
17166 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
17168 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
17170 if (con
->contype
== CONSTRAINT_FOREIGN
)
17173 Relation foreignrel
;
17175 /* the opposite end of what we used as scankey */
17176 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
17178 /* ignore if self-referencing */
17179 if (RelationGetRelid(rel
) == foreignrelid
)
17182 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
17186 if (!RelationIsPermanent(foreignrel
))
17188 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17189 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17190 RelationGetRelationName(rel
),
17191 RelationGetRelationName(foreignrel
)),
17192 errtableconstraint(rel
, NameStr(con
->conname
))));
17196 if (RelationIsPermanent(foreignrel
))
17198 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17199 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17200 RelationGetRelationName(rel
),
17201 RelationGetRelationName(foreignrel
)),
17202 errtableconstraint(rel
, NameStr(con
->conname
))));
17205 relation_close(foreignrel
, AccessShareLock
);
17209 systable_endscan(scan
);
17211 table_close(pg_constraint
, AccessShareLock
);
17217 * Execute ALTER TABLE SET SCHEMA
17220 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
17227 ObjectAddresses
*objsMoved
;
17228 ObjectAddress myself
;
17230 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
17231 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
17232 RangeVarCallbackForAlterRelation
,
17235 if (!OidIsValid(relid
))
17238 (errmsg("relation \"%s\" does not exist, skipping",
17239 stmt
->relation
->relname
)));
17240 return InvalidObjectAddress
;
17243 rel
= relation_open(relid
, NoLock
);
17245 oldNspOid
= RelationGetNamespace(rel
);
17247 /* If it's an owned sequence, disallow moving it by itself. */
17248 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
17253 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
17254 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
17256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17257 errmsg("cannot move an owned sequence into another schema"),
17258 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17259 RelationGetRelationName(rel
),
17260 get_rel_name(tableId
))));
17263 /* Get and lock schema OID and check its permissions. */
17264 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
17265 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
17267 /* common checks on switching namespaces */
17268 CheckSetNamespace(oldNspOid
, nspOid
);
17270 objsMoved
= new_object_addresses();
17271 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
17272 free_object_addresses(objsMoved
);
17274 ObjectAddressSet(myself
, RelationRelationId
, relid
);
17277 *oldschema
= oldNspOid
;
17279 /* close rel, but keep lock until commit */
17280 relation_close(rel
, NoLock
);
17286 * The guts of relocating a table or materialized view to another namespace:
17287 * besides moving the relation itself, its dependent objects are relocated to
17291 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
17292 ObjectAddresses
*objsMoved
)
17296 Assert(objsMoved
!= NULL
);
17298 /* OK, modify the pg_class row and pg_depend entry */
17299 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
17301 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
17302 nspOid
, true, objsMoved
);
17304 /* Fix the table's row type too, if it has one */
17305 if (OidIsValid(rel
->rd_rel
->reltype
))
17306 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
,
17307 nspOid
, false, false, objsMoved
);
17309 /* Fix other dependent stuff */
17310 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
||
17311 rel
->rd_rel
->relkind
== RELKIND_MATVIEW
||
17312 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
17314 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
17315 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
17316 objsMoved
, AccessExclusiveLock
);
17317 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
17321 table_close(classRel
, RowExclusiveLock
);
17325 * The guts of relocating a relation to another namespace: fix the pg_class
17326 * entry, and the pg_depend entry if any. Caller must already have
17327 * opened and write-locked pg_class.
17330 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
17331 Oid oldNspOid
, Oid newNspOid
,
17332 bool hasDependEntry
,
17333 ObjectAddresses
*objsMoved
)
17335 HeapTuple classTup
;
17336 Form_pg_class classForm
;
17337 ObjectAddress thisobj
;
17338 bool already_done
= false;
17340 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relOid
));
17341 if (!HeapTupleIsValid(classTup
))
17342 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
17343 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
17345 Assert(classForm
->relnamespace
== oldNspOid
);
17347 thisobj
.classId
= RelationRelationId
;
17348 thisobj
.objectId
= relOid
;
17349 thisobj
.objectSubId
= 0;
17352 * If the object has already been moved, don't move it again. If it's
17353 * already in the right place, don't move it, but still fire the object
17356 already_done
= object_address_present(&thisobj
, objsMoved
);
17357 if (!already_done
&& oldNspOid
!= newNspOid
)
17359 /* check for duplicate name (more friendly than unique-index failure) */
17360 if (get_relname_relid(NameStr(classForm
->relname
),
17361 newNspOid
) != InvalidOid
)
17363 (errcode(ERRCODE_DUPLICATE_TABLE
),
17364 errmsg("relation \"%s\" already exists in schema \"%s\"",
17365 NameStr(classForm
->relname
),
17366 get_namespace_name(newNspOid
))));
17368 /* classTup is a copy, so OK to scribble on */
17369 classForm
->relnamespace
= newNspOid
;
17371 CatalogTupleUpdate(classRel
, &classTup
->t_self
, classTup
);
17373 /* Update dependency on schema if caller said so */
17374 if (hasDependEntry
&&
17375 changeDependencyFor(RelationRelationId
,
17377 NamespaceRelationId
,
17380 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
17381 NameStr(classForm
->relname
));
17385 add_exact_object_address(&thisobj
, objsMoved
);
17387 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
17390 heap_freetuple(classTup
);
17394 * Move all indexes for the specified relation to another namespace.
17396 * Note: we assume adequate permission checking was done by the caller,
17397 * and that the caller has a suitable lock on the owning relation.
17400 AlterIndexNamespaces(Relation classRel
, Relation rel
,
17401 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
17406 indexList
= RelationGetIndexList(rel
);
17408 foreach(l
, indexList
)
17410 Oid indexOid
= lfirst_oid(l
);
17411 ObjectAddress thisobj
;
17413 thisobj
.classId
= RelationRelationId
;
17414 thisobj
.objectId
= indexOid
;
17415 thisobj
.objectSubId
= 0;
17418 * Note: currently, the index will not have its own dependency on the
17419 * namespace, so we don't need to do changeDependencyFor(). There's no
17420 * row type in pg_type, either.
17422 * XXX this objsMoved test may be pointless -- surely we have a single
17423 * dependency link from a relation to each index?
17425 if (!object_address_present(&thisobj
, objsMoved
))
17427 AlterRelationNamespaceInternal(classRel
, indexOid
,
17428 oldNspOid
, newNspOid
,
17430 add_exact_object_address(&thisobj
, objsMoved
);
17434 list_free(indexList
);
17438 * Move all identity and SERIAL-column sequences of the specified relation to another
17441 * Note: we assume adequate permission checking was done by the caller,
17442 * and that the caller has a suitable lock on the owning relation.
17445 AlterSeqNamespaces(Relation classRel
, Relation rel
,
17446 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
17451 ScanKeyData key
[2];
17455 * SERIAL sequences are those having an auto dependency on one of the
17456 * table's columns (we don't care *which* column, exactly).
17458 depRel
= table_open(DependRelationId
, AccessShareLock
);
17460 ScanKeyInit(&key
[0],
17461 Anum_pg_depend_refclassid
,
17462 BTEqualStrategyNumber
, F_OIDEQ
,
17463 ObjectIdGetDatum(RelationRelationId
));
17464 ScanKeyInit(&key
[1],
17465 Anum_pg_depend_refobjid
,
17466 BTEqualStrategyNumber
, F_OIDEQ
,
17467 ObjectIdGetDatum(RelationGetRelid(rel
)));
17468 /* we leave refobjsubid unspecified */
17470 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
17473 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
17475 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
17478 /* skip dependencies other than auto dependencies on columns */
17479 if (depForm
->refobjsubid
== 0 ||
17480 depForm
->classid
!= RelationRelationId
||
17481 depForm
->objsubid
!= 0 ||
17482 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
17485 /* Use relation_open just in case it's an index */
17486 seqRel
= relation_open(depForm
->objid
, lockmode
);
17488 /* skip non-sequence relations */
17489 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
17491 /* No need to keep the lock */
17492 relation_close(seqRel
, lockmode
);
17496 /* Fix the pg_class and pg_depend entries */
17497 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
17498 oldNspOid
, newNspOid
,
17502 * Sequences used to have entries in pg_type, but no longer do. If we
17503 * ever re-instate that, we'll need to move the pg_type entry to the
17504 * new namespace, too (using AlterTypeNamespaceInternal).
17506 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
17508 /* Now we can close it. Keep the lock till end of transaction. */
17509 relation_close(seqRel
, NoLock
);
17512 systable_endscan(scan
);
17514 relation_close(depRel
, AccessShareLock
);
17519 * This code supports
17520 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17522 * Because we only support this for TEMP tables, it's sufficient to remember
17523 * the state in a backend-local data structure.
17527 * Register a newly-created relation's ON COMMIT action.
17530 register_on_commit_action(Oid relid
, OnCommitAction action
)
17533 MemoryContext oldcxt
;
17536 * We needn't bother registering the relation unless there is an ON COMMIT
17537 * action we need to take.
17539 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
17542 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
17544 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
17546 oc
->oncommit
= action
;
17547 oc
->creating_subid
= GetCurrentSubTransactionId();
17548 oc
->deleting_subid
= InvalidSubTransactionId
;
17551 * We use lcons() here so that ON COMMIT actions are processed in reverse
17552 * order of registration. That might not be essential but it seems
17555 on_commits
= lcons(oc
, on_commits
);
17557 MemoryContextSwitchTo(oldcxt
);
17561 * Unregister any ON COMMIT action when a relation is deleted.
17563 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
17566 remove_on_commit_action(Oid relid
)
17570 foreach(l
, on_commits
)
17572 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17574 if (oc
->relid
== relid
)
17576 oc
->deleting_subid
= GetCurrentSubTransactionId();
17583 * Perform ON COMMIT actions.
17585 * This is invoked just before actually committing, since it's possible
17586 * to encounter errors.
17589 PreCommit_on_commit_actions(void)
17592 List
*oids_to_truncate
= NIL
;
17593 List
*oids_to_drop
= NIL
;
17595 foreach(l
, on_commits
)
17597 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17599 /* Ignore entry if already dropped in this xact */
17600 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
17603 switch (oc
->oncommit
)
17605 case ONCOMMIT_NOOP
:
17606 case ONCOMMIT_PRESERVE_ROWS
:
17607 /* Do nothing (there shouldn't be such entries, actually) */
17609 case ONCOMMIT_DELETE_ROWS
:
17612 * If this transaction hasn't accessed any temporary
17613 * relations, we can skip truncating ON COMMIT DELETE ROWS
17614 * tables, as they must still be empty.
17616 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
17617 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
17619 case ONCOMMIT_DROP
:
17620 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
17626 * Truncate relations before dropping so that all dependencies between
17627 * relations are removed after they are worked on. Doing it like this
17628 * might be a waste as it is possible that a relation being truncated will
17629 * be dropped anyway due to its parent being dropped, but this makes the
17630 * code more robust because of not having to re-check that the relation
17631 * exists at truncation time.
17633 if (oids_to_truncate
!= NIL
)
17634 heap_truncate(oids_to_truncate
);
17636 if (oids_to_drop
!= NIL
)
17638 ObjectAddresses
*targetObjects
= new_object_addresses();
17640 foreach(l
, oids_to_drop
)
17642 ObjectAddress object
;
17644 object
.classId
= RelationRelationId
;
17645 object
.objectId
= lfirst_oid(l
);
17646 object
.objectSubId
= 0;
17648 Assert(!object_address_present(&object
, targetObjects
));
17650 add_exact_object_address(&object
, targetObjects
);
17654 * Object deletion might involve toast table access (to clean up
17655 * toasted catalog entries), so ensure we have a valid snapshot.
17657 PushActiveSnapshot(GetTransactionSnapshot());
17660 * Since this is an automatic drop, rather than one directly initiated
17661 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17663 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
17664 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
17666 PopActiveSnapshot();
17668 #ifdef USE_ASSERT_CHECKING
17671 * Note that table deletion will call remove_on_commit_action, so the
17672 * entry should get marked as deleted.
17674 foreach(l
, on_commits
)
17676 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17678 if (oc
->oncommit
!= ONCOMMIT_DROP
)
17681 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
17688 * Post-commit or post-abort cleanup for ON COMMIT management.
17690 * All we do here is remove no-longer-needed OnCommitItem entries.
17692 * During commit, remove entries that were deleted during this transaction;
17693 * during abort, remove those created during this transaction.
17696 AtEOXact_on_commit_actions(bool isCommit
)
17698 ListCell
*cur_item
;
17700 foreach(cur_item
, on_commits
)
17702 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
17704 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
17705 oc
->creating_subid
!= InvalidSubTransactionId
)
17707 /* cur_item must be removed */
17708 on_commits
= foreach_delete_current(on_commits
, cur_item
);
17713 /* cur_item must be preserved */
17714 oc
->creating_subid
= InvalidSubTransactionId
;
17715 oc
->deleting_subid
= InvalidSubTransactionId
;
17721 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17723 * During subabort, we can immediately remove entries created during this
17724 * subtransaction. During subcommit, just relabel entries marked during
17725 * this subtransaction as being the parent's responsibility.
17728 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
17729 SubTransactionId parentSubid
)
17731 ListCell
*cur_item
;
17733 foreach(cur_item
, on_commits
)
17735 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
17737 if (!isCommit
&& oc
->creating_subid
== mySubid
)
17739 /* cur_item must be removed */
17740 on_commits
= foreach_delete_current(on_commits
, cur_item
);
17745 /* cur_item must be preserved */
17746 if (oc
->creating_subid
== mySubid
)
17747 oc
->creating_subid
= parentSubid
;
17748 if (oc
->deleting_subid
== mySubid
)
17749 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
17755 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17756 * the relation to be locked only if (1) it's a plain or partitioned table,
17757 * materialized view, or TOAST table and (2) the current user is the owner (or
17758 * the superuser). This meets the permission-checking needs of CLUSTER,
17759 * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it
17760 * can be used by all.
17763 RangeVarCallbackOwnsTable(const RangeVar
*relation
,
17764 Oid relId
, Oid oldRelId
, void *arg
)
17768 /* Nothing to do if the relation was not found. */
17769 if (!OidIsValid(relId
))
17773 * If the relation does exist, check whether it's an index. But note that
17774 * the relation might have been dropped between the time we did the name
17775 * lookup and now. In that case, there's nothing to do.
17777 relkind
= get_rel_relkind(relId
);
17780 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
17781 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
17783 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17784 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
17786 /* Check permissions */
17787 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
17788 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)), relation
->relname
);
17792 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17795 RangeVarCallbackForTruncate(const RangeVar
*relation
,
17796 Oid relId
, Oid oldRelId
, void *arg
)
17800 /* Nothing to do if the relation was not found. */
17801 if (!OidIsValid(relId
))
17804 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17805 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17806 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17808 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17809 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17811 ReleaseSysCache(tuple
);
17815 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17816 * the owner of the relation, or superuser.
17819 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
17820 Oid relId
, Oid oldRelId
, void *arg
)
17824 /* Nothing to do if the relation was not found. */
17825 if (!OidIsValid(relId
))
17828 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17829 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17830 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17832 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
17833 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
17834 relation
->relname
);
17836 if (!allowSystemTableMods
&&
17837 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
17839 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17840 errmsg("permission denied: \"%s\" is a system catalog",
17841 relation
->relname
)));
17843 ReleaseSysCache(tuple
);
17847 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17851 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
17854 Node
*stmt
= (Node
*) arg
;
17855 ObjectType reltype
;
17857 Form_pg_class classform
;
17858 AclResult aclresult
;
17861 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
17862 if (!HeapTupleIsValid(tuple
))
17863 return; /* concurrently dropped */
17864 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
17865 relkind
= classform
->relkind
;
17867 /* Must own relation. */
17868 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
17869 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
17871 /* No system table modifications unless explicitly allowed. */
17872 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
17874 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17875 errmsg("permission denied: \"%s\" is a system catalog",
17879 * Extract the specified relation type from the statement parse tree.
17881 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17882 * have CREATE rights on the containing namespace.
17884 if (IsA(stmt
, RenameStmt
))
17886 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
17887 GetUserId(), ACL_CREATE
);
17888 if (aclresult
!= ACLCHECK_OK
)
17889 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
17890 get_namespace_name(classform
->relnamespace
));
17891 reltype
= ((RenameStmt
*) stmt
)->renameType
;
17893 else if (IsA(stmt
, AlterObjectSchemaStmt
))
17894 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
17896 else if (IsA(stmt
, AlterTableStmt
))
17897 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
17900 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
17901 reltype
= OBJECT_TABLE
; /* placate compiler */
17905 * For compatibility with prior releases, we allow ALTER TABLE to be used
17906 * with most other types of relations (but not composite types). We allow
17907 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17908 * otherwise. Otherwise, the user must select the correct form of the
17909 * command for the relation at issue.
17911 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
17913 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17914 errmsg("\"%s\" is not a sequence", rv
->relname
)));
17916 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
17918 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17919 errmsg("\"%s\" is not a view", rv
->relname
)));
17921 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
17923 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17924 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
17926 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
17928 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17929 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
17931 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
17933 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17934 errmsg("\"%s\" is not a composite type", rv
->relname
)));
17936 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
17937 relkind
!= RELKIND_PARTITIONED_INDEX
17938 && !IsA(stmt
, RenameStmt
))
17940 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17941 errmsg("\"%s\" is not an index", rv
->relname
)));
17944 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17947 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
17949 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17950 errmsg("\"%s\" is a composite type", rv
->relname
),
17951 /* translator: %s is an SQL ALTER command */
17952 errhint("Use %s instead.",
17956 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17957 * to a different schema, such as indexes and TOAST tables.
17959 if (IsA(stmt
, AlterObjectSchemaStmt
))
17961 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
17963 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17964 errmsg("cannot change schema of index \"%s\"",
17966 errhint("Change the schema of the table instead.")));
17967 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
17969 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17970 errmsg("cannot change schema of composite type \"%s\"",
17972 /* translator: %s is an SQL ALTER command */
17973 errhint("Use %s instead.",
17975 else if (relkind
== RELKIND_TOASTVALUE
)
17977 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17978 errmsg("cannot change schema of TOAST table \"%s\"",
17980 errhint("Change the schema of the table instead.")));
17983 ReleaseSysCache(tuple
);
17987 * Transform any expressions present in the partition key
17989 * Returns a transformed PartitionSpec.
17991 static PartitionSpec
*
17992 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
17994 PartitionSpec
*newspec
;
17995 ParseState
*pstate
;
17996 ParseNamespaceItem
*nsitem
;
17999 newspec
= makeNode(PartitionSpec
);
18001 newspec
->strategy
= partspec
->strategy
;
18002 newspec
->partParams
= NIL
;
18003 newspec
->location
= partspec
->location
;
18005 /* Check valid number of columns for strategy */
18006 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
18007 list_length(partspec
->partParams
) != 1)
18009 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18010 errmsg("cannot use \"list\" partition strategy with more than one column")));
18013 * Create a dummy ParseState and insert the target relation as its sole
18014 * rangetable entry. We need a ParseState for transformExpr.
18016 pstate
= make_parsestate(NULL
);
18017 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
18018 NULL
, false, true);
18019 addNSItemToQuery(pstate
, nsitem
, true, true, true);
18021 /* take care of any partition expressions */
18022 foreach(l
, partspec
->partParams
)
18024 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
18028 /* Copy, to avoid scribbling on the input */
18029 pelem
= copyObject(pelem
);
18031 /* Now do parse transformation of the expression */
18032 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
18033 EXPR_KIND_PARTITION_EXPRESSION
);
18035 /* we have to fix its collations too */
18036 assign_expr_collations(pstate
, pelem
->expr
);
18039 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
18046 * Compute per-partition-column information from a list of PartitionElems.
18047 * Expressions in the PartitionElems must be parse-analyzed already.
18050 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
18051 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
18052 PartitionStrategy strategy
)
18059 foreach(lc
, partParams
)
18061 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
18065 if (pelem
->name
!= NULL
)
18067 /* Simple attribute reference */
18068 HeapTuple atttuple
;
18069 Form_pg_attribute attform
;
18071 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
18073 if (!HeapTupleIsValid(atttuple
))
18075 (errcode(ERRCODE_UNDEFINED_COLUMN
),
18076 errmsg("column \"%s\" named in partition key does not exist",
18078 parser_errposition(pstate
, pelem
->location
)));
18079 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
18081 if (attform
->attnum
<= 0)
18083 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18084 errmsg("cannot use system column \"%s\" in partition key",
18086 parser_errposition(pstate
, pelem
->location
)));
18089 * Generated columns cannot work: They are computed after BEFORE
18090 * triggers, but partition routing is done before all triggers.
18092 if (attform
->attgenerated
)
18094 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18095 errmsg("cannot use generated column in partition key"),
18096 errdetail("Column \"%s\" is a generated column.",
18098 parser_errposition(pstate
, pelem
->location
)));
18100 partattrs
[attn
] = attform
->attnum
;
18101 atttype
= attform
->atttypid
;
18102 attcollation
= attform
->attcollation
;
18103 ReleaseSysCache(atttuple
);
18108 Node
*expr
= pelem
->expr
;
18109 char partattname
[16];
18111 Assert(expr
!= NULL
);
18112 atttype
= exprType(expr
);
18113 attcollation
= exprCollation(expr
);
18116 * The expression must be of a storable type (e.g., not RECORD).
18117 * The test is the same as for whether a table column is of a safe
18118 * type (which is why we needn't check for the non-expression
18121 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
18122 CheckAttributeType(partattname
,
18123 atttype
, attcollation
,
18124 NIL
, CHKATYPE_IS_PARTKEY
);
18127 * Strip any top-level COLLATE clause. This ensures that we treat
18128 * "x COLLATE y" and "(x COLLATE y)" alike.
18130 while (IsA(expr
, CollateExpr
))
18131 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
18133 if (IsA(expr
, Var
) &&
18134 ((Var
*) expr
)->varattno
> 0)
18137 * User wrote "(column)" or "(column COLLATE something)".
18138 * Treat it like simple attribute anyway.
18140 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
18144 Bitmapset
*expr_attrs
= NULL
;
18147 partattrs
[attn
] = 0; /* marks the column as expression */
18148 *partexprs
= lappend(*partexprs
, expr
);
18151 * Try to simplify the expression before checking for
18152 * mutability. The main practical value of doing it in this
18153 * order is that an inline-able SQL-language function will be
18154 * accepted if its expansion is immutable, whether or not the
18155 * function itself is marked immutable.
18157 * Note that expression_planner does not change the passed in
18158 * expression destructively and we have already saved the
18159 * expression to be stored into the catalog above.
18161 expr
= (Node
*) expression_planner((Expr
*) expr
);
18164 * Partition expression cannot contain mutable functions,
18165 * because a given row must always map to the same partition
18166 * as long as there is no change in the partition boundary
18169 if (contain_mutable_functions(expr
))
18171 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18172 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18175 * transformPartitionSpec() should have already rejected
18176 * subqueries, aggregates, window functions, and SRFs, based
18177 * on the EXPR_KIND_ for partition expressions.
18181 * Cannot allow system column references, since that would
18182 * make partition routing impossible: their values won't be
18183 * known yet when we need to do that.
18185 pull_varattnos(expr
, 1, &expr_attrs
);
18186 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
18188 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
18191 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18192 errmsg("partition key expressions cannot contain system column references")));
18196 * Generated columns cannot work: They are computed after
18197 * BEFORE triggers, but partition routing is done before all
18201 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
18203 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
18206 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
18208 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18209 errmsg("cannot use generated column in partition key"),
18210 errdetail("Column \"%s\" is a generated column.",
18211 get_attname(RelationGetRelid(rel
), attno
, false)),
18212 parser_errposition(pstate
, pelem
->location
)));
18216 * While it is not exactly *wrong* for a partition expression
18217 * to be a constant, it seems better to reject such keys.
18219 if (IsA(expr
, Const
))
18221 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18222 errmsg("cannot use constant expression as partition key")));
18227 * Apply collation override if any
18229 if (pelem
->collation
)
18230 attcollation
= get_collation_oid(pelem
->collation
, false);
18233 * Check we have a collation iff it's a collatable type. The only
18234 * expected failures here are (1) COLLATE applied to a noncollatable
18235 * type, or (2) partition expression had an unresolved collation. But
18236 * we might as well code this to be a complete consistency check.
18238 if (type_is_collatable(atttype
))
18240 if (!OidIsValid(attcollation
))
18242 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
18243 errmsg("could not determine which collation to use for partition expression"),
18244 errhint("Use the COLLATE clause to set the collation explicitly.")));
18248 if (OidIsValid(attcollation
))
18250 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18251 errmsg("collations are not supported by type %s",
18252 format_type_be(atttype
))));
18255 partcollation
[attn
] = attcollation
;
18258 * Identify the appropriate operator class. For list and range
18259 * partitioning, we use a btree operator class; hash partitioning uses
18260 * a hash operator class.
18262 if (strategy
== PARTITION_STRATEGY_HASH
)
18263 am_oid
= HASH_AM_OID
;
18265 am_oid
= BTREE_AM_OID
;
18267 if (!pelem
->opclass
)
18269 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
18271 if (!OidIsValid(partopclass
[attn
]))
18273 if (strategy
== PARTITION_STRATEGY_HASH
)
18275 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18276 errmsg("data type %s has no default operator class for access method \"%s\"",
18277 format_type_be(atttype
), "hash"),
18278 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18281 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18282 errmsg("data type %s has no default operator class for access method \"%s\"",
18283 format_type_be(atttype
), "btree"),
18284 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18288 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
18290 am_oid
== HASH_AM_OID
? "hash" : "btree",
18298 * PartConstraintImpliedByRelConstraint
18299 * Do scanrel's existing constraints imply the partition constraint?
18301 * "Existing constraints" include its check constraints and column-level
18302 * not-null constraints. partConstraint describes the partition constraint,
18303 * in implicit-AND form.
18306 PartConstraintImpliedByRelConstraint(Relation scanrel
,
18307 List
*partConstraint
)
18309 List
*existConstraint
= NIL
;
18310 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18313 if (constr
&& constr
->has_not_null
)
18315 int natts
= scanrel
->rd_att
->natts
;
18317 for (i
= 1; i
<= natts
; i
++)
18319 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
18321 if (att
->attnotnull
&& !att
->attisdropped
)
18323 NullTest
*ntest
= makeNode(NullTest
);
18325 ntest
->arg
= (Expr
*) makeVar(1,
18331 ntest
->nulltesttype
= IS_NOT_NULL
;
18334 * argisrow=false is correct even for a composite column,
18335 * because attnotnull does not represent a SQL-spec IS NOT
18336 * NULL test in such a case, just IS DISTINCT FROM NULL.
18338 ntest
->argisrow
= false;
18339 ntest
->location
= -1;
18340 existConstraint
= lappend(existConstraint
, ntest
);
18345 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
18349 * ConstraintImpliedByRelConstraint
18350 * Do scanrel's existing constraints imply the given constraint?
18352 * testConstraint is the constraint to validate. provenConstraint is a
18353 * caller-provided list of conditions which this function may assume
18354 * to be true. Both provenConstraint and testConstraint must be in
18355 * implicit-AND form, must only contain immutable clauses, and must
18356 * contain only Vars with varno = 1.
18359 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
18361 List
*existConstraint
= list_copy(provenConstraint
);
18362 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18366 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
18367 for (i
= 0; i
< num_check
; i
++)
18372 * If this constraint hasn't been fully validated yet, we must ignore
18375 if (!constr
->check
[i
].ccvalid
)
18378 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
18381 * Run each expression through const-simplification and
18382 * canonicalization. It is necessary, because we will be comparing it
18383 * to similarly-processed partition constraint expressions, and may
18384 * fail to detect valid matches without this.
18386 cexpr
= eval_const_expressions(NULL
, cexpr
);
18387 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
18389 existConstraint
= list_concat(existConstraint
,
18390 make_ands_implicit((Expr
*) cexpr
));
18394 * Try to make the proof. Since we are comparing CHECK constraints, we
18395 * need to use weak implication, i.e., we assume existConstraint is
18396 * not-false and try to prove the same for testConstraint.
18398 * Note that predicate_implied_by assumes its first argument is known
18399 * immutable. That should always be true for both NOT NULL and partition
18400 * constraints, so we don't test it here.
18402 return predicate_implied_by(testConstraint
, existConstraint
, true);
18406 * QueuePartitionConstraintValidation
18408 * Add an entry to wqueue to have the given partition constraint validated by
18409 * Phase 3, for the given relation, and all its children.
18411 * We first verify whether the given constraint is implied by pre-existing
18412 * relation constraints; if it is, there's no need to scan the table to
18413 * validate, so don't queue in that case.
18416 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
18417 List
*partConstraint
,
18418 bool validate_default
)
18421 * Based on the table's existing constraints, determine whether or not we
18422 * may skip scanning the table.
18424 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
18426 if (!validate_default
)
18428 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18429 RelationGetRelationName(scanrel
))));
18432 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18433 RelationGetRelationName(scanrel
))));
18438 * Constraints proved insufficient. For plain relations, queue a
18439 * validation item now; for partitioned tables, recurse to process each
18442 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
18444 AlteredTableInfo
*tab
;
18446 /* Grab a work queue entry. */
18447 tab
= ATGetQueueEntry(wqueue
, scanrel
);
18448 Assert(tab
->partition_constraint
== NULL
);
18449 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
18450 tab
->validate_default
= validate_default
;
18452 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18454 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
18457 for (i
= 0; i
< partdesc
->nparts
; i
++)
18460 List
*thisPartConstraint
;
18463 * This is the minimum lock we need to prevent deadlocks.
18465 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
18468 * Adjust the constraint for scanrel so that it matches this
18469 * partition's attribute numbers.
18471 thisPartConstraint
=
18472 map_partition_varattnos(partConstraint
, 1,
18473 part_rel
, scanrel
);
18475 QueuePartitionConstraintValidation(wqueue
, part_rel
,
18476 thisPartConstraint
,
18478 table_close(part_rel
, NoLock
); /* keep lock till commit */
18484 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18486 * Return the address of the newly attached partition.
18488 static ObjectAddress
18489 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
18490 AlterTableUtilityContext
*context
)
18492 Relation attachrel
,
18494 List
*attachrel_children
;
18495 List
*partConstraint
;
18500 TupleDesc tupleDesc
;
18501 ObjectAddress address
;
18502 const char *trigger_name
;
18503 Oid defaultPartOid
;
18504 List
*partBoundConstraint
;
18505 ParseState
*pstate
= make_parsestate(NULL
);
18507 pstate
->p_sourcetext
= context
->queryString
;
18510 * We must lock the default partition if one exists, because attaching a
18511 * new partition will change its partition constraint.
18514 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
18515 if (OidIsValid(defaultPartOid
))
18516 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
18518 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
18521 * XXX I think it'd be a good idea to grab locks on all tables referenced
18522 * by FKs at this point also.
18526 * Must be owner of both parent and source table -- parent was checked by
18527 * ATSimplePermissions call in ATPrepCmd
18529 ATSimplePermissions(AT_AttachPartition
, attachrel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
18531 /* A partition can only have one parent */
18532 if (attachrel
->rd_rel
->relispartition
)
18534 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18535 errmsg("\"%s\" is already a partition",
18536 RelationGetRelationName(attachrel
))));
18538 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
18540 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18541 errmsg("cannot attach a typed table as partition")));
18544 * Table being attached should not already be part of inheritance; either
18545 * as a child table...
18547 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
18549 Anum_pg_inherits_inhrelid
,
18550 BTEqualStrategyNumber
, F_OIDEQ
,
18551 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
18552 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
18554 if (HeapTupleIsValid(systable_getnext(scan
)))
18556 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18557 errmsg("cannot attach inheritance child as partition")));
18558 systable_endscan(scan
);
18560 /* ...or as a parent table (except the case when it is partitioned) */
18562 Anum_pg_inherits_inhparent
,
18563 BTEqualStrategyNumber
, F_OIDEQ
,
18564 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
18565 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
18567 if (HeapTupleIsValid(systable_getnext(scan
)) &&
18568 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
18570 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18571 errmsg("cannot attach inheritance parent as partition")));
18572 systable_endscan(scan
);
18573 table_close(catalog
, AccessShareLock
);
18576 * Prevent circularity by seeing if rel is a partition of attachrel. (In
18577 * particular, this disallows making a rel a partition of itself.)
18579 * We do that by checking if rel is a member of the list of attachrel's
18580 * partitions provided the latter is partitioned at all. We want to avoid
18581 * having to construct this list again, so we request the strongest lock
18582 * on all partitions. We need the strongest lock, because we may decide
18583 * to scan them if we find out that the table being attached (or its leaf
18584 * partitions) may contain rows that violate the partition constraint. If
18585 * the table has a constraint that would prevent such rows, which by
18586 * definition is present in all the partitions, we need not scan the
18587 * table, nor its partitions. But we cannot risk a deadlock by taking a
18588 * weaker lock now and the stronger one only when needed.
18590 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
18591 AccessExclusiveLock
, NULL
);
18592 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
18594 (errcode(ERRCODE_DUPLICATE_TABLE
),
18595 errmsg("circular inheritance not allowed"),
18596 errdetail("\"%s\" is already a child of \"%s\".",
18597 RelationGetRelationName(rel
),
18598 RelationGetRelationName(attachrel
))));
18600 /* If the parent is permanent, so must be all of its partitions. */
18601 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
18602 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
18604 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18605 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18606 RelationGetRelationName(rel
))));
18608 /* Temp parent cannot have a partition that is itself not a temp */
18609 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18610 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
18612 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18613 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18614 RelationGetRelationName(rel
))));
18616 /* If the parent is temp, it must belong to this session */
18617 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18618 !rel
->rd_islocaltemp
)
18620 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18621 errmsg("cannot attach as partition of temporary relation of another session")));
18623 /* Ditto for the partition */
18624 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18625 !attachrel
->rd_islocaltemp
)
18627 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18628 errmsg("cannot attach temporary relation of another session as partition")));
18630 /* Check if there are any columns in attachrel that aren't in the parent */
18631 tupleDesc
= RelationGetDescr(attachrel
);
18632 natts
= tupleDesc
->natts
;
18633 for (attno
= 1; attno
<= natts
; attno
++)
18635 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
18636 char *attributeName
= NameStr(attribute
->attname
);
18638 /* Ignore dropped */
18639 if (attribute
->attisdropped
)
18642 /* Try to find the column in parent (matching on column name) */
18643 if (!SearchSysCacheExists2(ATTNAME
,
18644 ObjectIdGetDatum(RelationGetRelid(rel
)),
18645 CStringGetDatum(attributeName
)))
18647 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18648 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18649 RelationGetRelationName(attachrel
), attributeName
,
18650 RelationGetRelationName(rel
)),
18651 errdetail("The new partition may contain only the columns present in parent.")));
18655 * If child_rel has row-level triggers with transition tables, we
18656 * currently don't allow it to become a partition. See also prohibitions
18657 * in ATExecAddInherit() and CreateTrigger().
18659 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
18660 if (trigger_name
!= NULL
)
18662 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
18663 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18664 trigger_name
, RelationGetRelationName(attachrel
)),
18665 errdetail("ROW triggers with transition tables are not supported on partitions.")));
18668 * Check that the new partition's bound is valid and does not overlap any
18669 * of existing partitions of the parent - note that it does not return on
18672 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
18673 cmd
->bound
, pstate
);
18675 /* OK to create inheritance. Rest of the checks performed there */
18676 CreateInheritance(attachrel
, rel
);
18678 /* Update the pg_class entry. */
18679 StorePartitionBound(attachrel
, rel
, cmd
->bound
);
18681 /* Ensure there exists a correct set of indexes in the partition. */
18682 AttachPartitionEnsureIndexes(wqueue
, rel
, attachrel
);
18685 CloneRowTriggersToPartition(rel
, attachrel
);
18688 * Clone foreign key constraints. Callee is responsible for setting up
18689 * for phase 3 constraint verification.
18691 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
18694 * Generate partition constraint from the partition bound specification.
18695 * If the parent itself is a partition, make sure to include its
18696 * constraint as well.
18698 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
18699 partConstraint
= list_concat(partBoundConstraint
,
18700 RelationGetPartitionQual(rel
));
18702 /* Skip validation if there are no constraints to validate. */
18703 if (partConstraint
)
18706 * Run the partition quals through const-simplification similar to
18707 * check constraints. We skip canonicalize_qual, though, because
18708 * partition quals should be in canonical form already.
18711 (List
*) eval_const_expressions(NULL
,
18712 (Node
*) partConstraint
);
18714 /* XXX this sure looks wrong */
18715 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
18718 * Adjust the generated constraint to match this partition's attribute
18721 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
18724 /* Validate partition constraints against the table being attached. */
18725 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
18730 * If we're attaching a partition other than the default partition and a
18731 * default one exists, then that partition's partition constraint changes,
18732 * so add an entry to the work queue to validate it, too. (We must not do
18733 * this when the partition being attached is the default one; we already
18736 if (OidIsValid(defaultPartOid
))
18738 Relation defaultrel
;
18739 List
*defPartConstraint
;
18741 Assert(!cmd
->bound
->is_default
);
18743 /* we already hold a lock on the default partition */
18744 defaultrel
= table_open(defaultPartOid
, NoLock
);
18745 defPartConstraint
=
18746 get_proposed_default_constraint(partBoundConstraint
);
18749 * Map the Vars in the constraint expression from rel's attnos to
18752 defPartConstraint
=
18753 map_partition_varattnos(defPartConstraint
,
18754 1, defaultrel
, rel
);
18755 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
18756 defPartConstraint
, true);
18758 /* keep our lock until commit. */
18759 table_close(defaultrel
, NoLock
);
18762 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
18765 * If the partition we just attached is partitioned itself, invalidate
18766 * relcache for all descendent partitions too to ensure that their
18767 * rd_partcheck expression trees are rebuilt; partitions already locked at
18768 * the beginning of this function.
18770 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18774 foreach(l
, attachrel_children
)
18776 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
18780 /* keep our lock until commit */
18781 table_close(attachrel
, NoLock
);
18787 * AttachPartitionEnsureIndexes
18788 * subroutine for ATExecAttachPartition to create/match indexes
18790 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18791 * PARTITION: every partition must have an index attached to each index on the
18792 * partitioned table.
18795 AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
)
18798 List
*attachRelIdxs
;
18799 Relation
*attachrelIdxRels
;
18800 IndexInfo
**attachInfos
;
18803 MemoryContext oldcxt
;
18805 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
18806 "AttachPartitionEnsureIndexes",
18807 ALLOCSET_DEFAULT_SIZES
);
18808 oldcxt
= MemoryContextSwitchTo(cxt
);
18810 idxes
= RelationGetIndexList(rel
);
18811 attachRelIdxs
= RelationGetIndexList(attachrel
);
18812 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
18813 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
18815 /* Build arrays of all existing indexes and their IndexInfos */
18816 foreach(cell
, attachRelIdxs
)
18818 Oid cldIdxId
= lfirst_oid(cell
);
18819 int i
= foreach_current_index(cell
);
18821 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
18822 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
18826 * If we're attaching a foreign table, we must fail if any of the indexes
18827 * is a constraint index; otherwise, there's nothing to do here. Do this
18828 * before starting work, to avoid wasting the effort of building a few
18829 * non-unique indexes before coming across a unique one.
18831 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
18833 foreach(cell
, idxes
)
18835 Oid idx
= lfirst_oid(cell
);
18836 Relation idxRel
= index_open(idx
, AccessShareLock
);
18838 if (idxRel
->rd_index
->indisunique
||
18839 idxRel
->rd_index
->indisprimary
)
18841 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18842 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18843 RelationGetRelationName(attachrel
),
18844 RelationGetRelationName(rel
)),
18845 errdetail("Partitioned table \"%s\" contains unique indexes.",
18846 RelationGetRelationName(rel
))));
18847 index_close(idxRel
, AccessShareLock
);
18854 * For each index on the partitioned table, find a matching one in the
18855 * partition-to-be; if one is not found, create one.
18857 foreach(cell
, idxes
)
18859 Oid idx
= lfirst_oid(cell
);
18860 Relation idxRel
= index_open(idx
, AccessShareLock
);
18863 bool found
= false;
18867 * Ignore indexes in the partitioned table other than partitioned
18870 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
18872 index_close(idxRel
, AccessShareLock
);
18876 /* construct an indexinfo to compare existing indexes against */
18877 info
= BuildIndexInfo(idxRel
);
18878 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
18879 RelationGetDescr(rel
),
18881 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
18884 * Scan the list of existing indexes in the partition-to-be, and mark
18885 * the first matching, valid, unattached one we find, if any, as
18886 * partition of the parent index. If we find one, we're done.
18888 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
18890 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
18891 Oid cldConstrOid
= InvalidOid
;
18893 /* does this index have a parent? if so, can't use it */
18894 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
18897 /* If this index is invalid, can't use it */
18898 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
18901 if (CompareIndexInfo(attachInfos
[i
], info
,
18902 attachrelIdxRels
[i
]->rd_indcollation
,
18903 idxRel
->rd_indcollation
,
18904 attachrelIdxRels
[i
]->rd_opfamily
,
18905 idxRel
->rd_opfamily
,
18909 * If this index is being created in the parent because of a
18910 * constraint, then the child needs to have a constraint also,
18911 * so look for one. If there is no such constraint, this
18912 * index is no good, so keep looking.
18914 if (OidIsValid(constraintOid
))
18917 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
18920 if (!OidIsValid(cldConstrOid
))
18925 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
18926 if (OidIsValid(constraintOid
))
18927 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
18928 RelationGetRelid(attachrel
));
18931 CommandCounterIncrement();
18937 * If no suitable index was found in the partition-to-be, create one
18945 stmt
= generateClonedIndexStmt(NULL
,
18950 * If the index is a primary key, mark all columns as NOT NULL if
18951 * they aren't already.
18955 MemoryContextSwitchTo(oldcxt
);
18956 for (int j
= 0; j
< info
->ii_NumIndexKeyAttrs
; j
++)
18958 AttrNumber childattno
;
18960 childattno
= get_attnum(RelationGetRelid(attachrel
),
18961 get_attname(RelationGetRelid(rel
),
18962 info
->ii_IndexAttrNumbers
[j
],
18964 set_attnotnull(wqueue
, attachrel
, childattno
,
18965 true, AccessExclusiveLock
);
18967 MemoryContextSwitchTo(cxt
);
18970 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
18971 RelationGetRelid(idxRel
),
18974 true, false, false, false, false);
18977 index_close(idxRel
, AccessShareLock
);
18982 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
18983 index_close(attachrelIdxRels
[i
], AccessShareLock
);
18984 MemoryContextSwitchTo(oldcxt
);
18985 MemoryContextDelete(cxt
);
18989 * CloneRowTriggersToPartition
18990 * subroutine for ATExecAttachPartition/DefineRelation to create row
18991 * triggers on partitions
18994 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
18996 Relation pg_trigger
;
19000 MemoryContext perTupCxt
;
19002 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19003 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
19004 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
19005 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
19006 true, NULL
, 1, &key
);
19008 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
19009 "clone trig", ALLOCSET_SMALL_SIZES
);
19011 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
19013 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
19014 CreateTrigStmt
*trigStmt
;
19019 List
*trigargs
= NIL
;
19020 MemoryContext oldcxt
;
19023 * Ignore statement-level triggers; those are not cloned.
19025 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
19029 * Don't clone internal triggers, because the constraint cloning code
19032 if (trigForm
->tgisinternal
)
19036 * Complain if we find an unexpected trigger type.
19038 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
19039 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
19040 elog(ERROR
, "unexpected trigger \"%s\" found",
19041 NameStr(trigForm
->tgname
));
19043 /* Use short-lived context for CREATE TRIGGER */
19044 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
19047 * If there is a WHEN clause, generate a 'cooked' version of it that's
19048 * appropriate for the partition.
19050 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
19051 RelationGetDescr(pg_trigger
), &isnull
);
19054 qual
= stringToNode(TextDatumGetCString(value
));
19055 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
19056 partition
, parent
);
19057 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
19058 partition
, parent
);
19062 * If there is a column list, transform it to a list of column names.
19063 * Note we don't need to map this list in any way ...
19065 if (trigForm
->tgattr
.dim1
> 0)
19069 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
19071 Form_pg_attribute col
;
19073 col
= TupleDescAttr(parent
->rd_att
,
19074 trigForm
->tgattr
.values
[i
] - 1);
19075 cols
= lappend(cols
,
19076 makeString(pstrdup(NameStr(col
->attname
))));
19080 /* Reconstruct trigger arguments list. */
19081 if (trigForm
->tgnargs
> 0)
19085 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
19086 RelationGetDescr(pg_trigger
), &isnull
);
19088 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19089 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
19091 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
19093 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
19095 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
19096 p
+= strlen(p
) + 1;
19100 trigStmt
= makeNode(CreateTrigStmt
);
19101 trigStmt
->replace
= false;
19102 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
19103 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
19104 trigStmt
->relation
= NULL
;
19105 trigStmt
->funcname
= NULL
; /* passed separately */
19106 trigStmt
->args
= trigargs
;
19107 trigStmt
->row
= true;
19108 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
19109 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
19110 trigStmt
->columns
= cols
;
19111 trigStmt
->whenClause
= NULL
; /* passed separately */
19112 trigStmt
->transitionRels
= NIL
; /* not supported at present */
19113 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
19114 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
19115 trigStmt
->constrrel
= NULL
; /* passed separately */
19117 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
19118 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
19119 trigForm
->tgfoid
, trigForm
->oid
, qual
,
19120 false, true, trigForm
->tgenabled
);
19122 MemoryContextSwitchTo(oldcxt
);
19123 MemoryContextReset(perTupCxt
);
19126 MemoryContextDelete(perTupCxt
);
19128 systable_endscan(scan
);
19129 table_close(pg_trigger
, RowExclusiveLock
);
19133 * ALTER TABLE DETACH PARTITION
19135 * Return the address of the relation that is no longer a partition of rel.
19137 * If concurrent mode is requested, we run in two transactions. A side-
19138 * effect is that this command cannot run in a multi-part ALTER TABLE.
19139 * Currently, that's enforced by the grammar.
19141 * The strategy for concurrency is to first modify the partition's
19142 * pg_inherit catalog row to make it visible to everyone that the
19143 * partition is detached, lock the partition against writes, and commit
19144 * the transaction; anyone who requests the partition descriptor from
19145 * that point onwards has to ignore such a partition. In a second
19146 * transaction, we wait until all transactions that could have seen the
19147 * partition as attached are gone, then we remove the rest of partition
19148 * metadata (pg_inherits and pg_class.relpartbounds).
19150 static ObjectAddress
19151 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
19152 RangeVar
*name
, bool concurrent
)
19155 ObjectAddress address
;
19156 Oid defaultPartOid
;
19159 * We must lock the default partition, because detaching this partition
19160 * will change its partition constraint.
19163 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19164 if (OidIsValid(defaultPartOid
))
19167 * Concurrent detaching when a default partition exists is not
19168 * supported. The main problem is that the default partition
19169 * constraint would change. And there's a definitional problem: what
19170 * should happen to the tuples that are being inserted that belong to
19171 * the partition being detached? Putting them on the partition being
19172 * detached would be wrong, since they'd become "lost" after the
19173 * detaching completes but we cannot put them in the default partition
19174 * either until we alter its partition constraint.
19176 * I think we could solve this problem if we effected the constraint
19177 * change before committing the first transaction. But the lock would
19178 * have to remain AEL and it would cause concurrent query planning to
19179 * be blocked, so changing it that way would be even worse.
19183 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19184 errmsg("cannot detach partitions concurrently when a default partition exists")));
19185 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19189 * In concurrent mode, the partition is locked with share-update-exclusive
19190 * in the first transaction. This allows concurrent transactions to be
19191 * doing DML to the partition.
19193 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
19194 AccessExclusiveLock
);
19197 * Check inheritance conditions and either delete the pg_inherits row (in
19198 * non-concurrent mode) or just set the inhdetachpending flag.
19201 RemoveInheritance(partRel
, rel
, false);
19203 MarkInheritDetached(partRel
, rel
);
19206 * Ensure that foreign keys still hold after this detach. This keeps
19207 * locks on the referencing tables, which prevents concurrent transactions
19208 * from adding rows that we wouldn't see. For this to work in concurrent
19209 * mode, it is critical that the partition appears as no longer attached
19210 * for the RI queries as soon as the first transaction commits.
19212 ATDetachCheckNoForeignKeyRefs(partRel
);
19215 * Concurrent mode has to work harder; first we add a new constraint to
19216 * the partition that matches the partition constraint. Then we close our
19217 * existing transaction, and in a new one wait for all processes to catch
19218 * up on the catalog updates we've done so far; at that point we can
19219 * complete the operation.
19226 char *parentrelname
;
19230 * Add a new constraint to the partition being detached, which
19231 * supplants the partition constraint (unless there is one already).
19233 DetachAddConstraintIfNeeded(wqueue
, partRel
);
19236 * We're almost done now; the only traces that remain are the
19237 * pg_inherits tuple and the partition's relpartbounds. Before we can
19238 * remove those, we need to wait until all transactions that know that
19239 * this is a partition are gone.
19243 * Remember relation OIDs to re-acquire them later; and relation names
19244 * too, for error messages if something is dropped in between.
19246 partrelid
= RelationGetRelid(partRel
);
19247 parentrelid
= RelationGetRelid(rel
);
19248 parentrelname
= MemoryContextStrdup(PortalContext
,
19249 RelationGetRelationName(rel
));
19250 partrelname
= MemoryContextStrdup(PortalContext
,
19251 RelationGetRelationName(partRel
));
19253 /* Invalidate relcache entries for the parent -- must be before close */
19254 CacheInvalidateRelcache(rel
);
19256 table_close(partRel
, NoLock
);
19257 table_close(rel
, NoLock
);
19260 /* Make updated catalog entry visible */
19261 PopActiveSnapshot();
19262 CommitTransactionCommand();
19264 StartTransactionCommand();
19267 * Now wait. This ensures that all queries that were planned
19268 * including the partition are finished before we remove the rest of
19269 * catalog entries. We don't need or indeed want to acquire this
19270 * lock, though -- that would block later queries.
19272 * We don't need to concern ourselves with waiting for a lock on the
19273 * partition itself, since we will acquire AccessExclusiveLock below.
19275 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
19276 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
19279 * Now acquire locks in both relations again. Note they may have been
19280 * removed in the meantime, so care is required.
19282 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
19283 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
19285 /* If the relations aren't there, something bad happened; bail out */
19288 if (partRel
!= NULL
) /* shouldn't happen */
19289 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
19292 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19293 errmsg("partitioned table \"%s\" was removed concurrently",
19296 if (partRel
== NULL
)
19298 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19299 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
19304 /* Do the final part of detaching */
19305 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
19307 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19309 /* keep our lock until commit */
19310 table_close(partRel
, NoLock
);
19316 * Second part of ALTER TABLE .. DETACH.
19318 * This is separate so that it can be run independently when the second
19319 * transaction of the concurrent algorithm fails (crash or abort).
19322 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
19323 Oid defaultPartOid
)
19329 Datum new_val
[Natts_pg_class
];
19330 bool new_null
[Natts_pg_class
],
19331 new_repl
[Natts_pg_class
];
19334 Relation trigrel
= NULL
;
19339 * We can remove the pg_inherits row now. (In the non-concurrent case,
19340 * this was already done).
19342 RemoveInheritance(partRel
, rel
, true);
19345 /* Drop any triggers that were cloned on creation/attach. */
19346 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
19349 * Detach any foreign keys that are inherited. This includes creating
19350 * additional action triggers.
19352 fks
= copyObject(RelationGetFKeyList(partRel
));
19354 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19357 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
19359 Form_pg_constraint conform
;
19360 Constraint
*fkconstraint
;
19361 Oid insertTriggerOid
,
19364 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
19365 if (!HeapTupleIsValid(contup
))
19366 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
19367 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
19369 /* consider only the inherited foreign keys */
19370 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
19371 !OidIsValid(conform
->conparentid
))
19373 ReleaseSysCache(contup
);
19377 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19378 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
19381 * Also, look up the partition's "check" triggers corresponding to the
19382 * constraint being detached and detach them from the parent triggers.
19384 GetForeignKeyCheckTriggers(trigrel
,
19385 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
19386 &insertTriggerOid
, &updateTriggerOid
);
19387 Assert(OidIsValid(insertTriggerOid
));
19388 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
19389 RelationGetRelid(partRel
));
19390 Assert(OidIsValid(updateTriggerOid
));
19391 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
19392 RelationGetRelid(partRel
));
19395 * Make the action triggers on the referenced relation. When this was
19396 * a partition the action triggers pointed to the parent rel (they
19397 * still do), but now we need separate ones of our own.
19399 fkconstraint
= makeNode(Constraint
);
19400 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
19401 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
19402 fkconstraint
->deferrable
= conform
->condeferrable
;
19403 fkconstraint
->initdeferred
= conform
->condeferred
;
19404 fkconstraint
->location
= -1;
19405 fkconstraint
->pktable
= NULL
;
19406 fkconstraint
->fk_attrs
= NIL
;
19407 fkconstraint
->pk_attrs
= NIL
;
19408 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
19409 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
19410 fkconstraint
->fk_del_action
= conform
->confdeltype
;
19411 fkconstraint
->fk_del_set_cols
= NIL
;
19412 fkconstraint
->old_conpfeqop
= NIL
;
19413 fkconstraint
->old_pktable_oid
= InvalidOid
;
19414 fkconstraint
->skip_validation
= false;
19415 fkconstraint
->initially_valid
= true;
19417 createForeignKeyActionTriggers(partRel
, conform
->confrelid
,
19418 fkconstraint
, fk
->conoid
,
19420 InvalidOid
, InvalidOid
,
19423 ReleaseSysCache(contup
);
19425 list_free_deep(fks
);
19427 table_close(trigrel
, RowExclusiveLock
);
19430 * Any sub-constraints that are in the referenced-side of a larger
19431 * constraint have to be removed. This partition is no longer part of the
19432 * key space of the constraint.
19434 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
19436 Oid constrOid
= lfirst_oid(cell
);
19437 ObjectAddress constraint
;
19439 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
19440 deleteDependencyRecordsForClass(ConstraintRelationId
,
19442 ConstraintRelationId
,
19443 DEPENDENCY_INTERNAL
);
19444 CommandCounterIncrement();
19446 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
19447 performDeletion(&constraint
, DROP_RESTRICT
, 0);
19450 /* Now we can detach indexes */
19451 indexes
= RelationGetIndexList(partRel
);
19452 foreach(cell
, indexes
)
19454 Oid idxid
= lfirst_oid(cell
);
19458 if (!has_superclass(idxid
))
19461 Assert((IndexGetRelation(get_partition_parent(idxid
, false), false) ==
19462 RelationGetRelid(rel
)));
19464 idx
= index_open(idxid
, AccessExclusiveLock
);
19465 IndexSetParentIndex(idx
, InvalidOid
);
19467 /* If there's a constraint associated with the index, detach it too */
19468 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
19470 if (OidIsValid(constrOid
))
19471 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
19473 index_close(idx
, NoLock
);
19476 /* Update pg_class tuple */
19477 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
19478 tuple
= SearchSysCacheCopy1(RELOID
,
19479 ObjectIdGetDatum(RelationGetRelid(partRel
)));
19480 if (!HeapTupleIsValid(tuple
))
19481 elog(ERROR
, "cache lookup failed for relation %u",
19482 RelationGetRelid(partRel
));
19483 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
19485 /* Clear relpartbound and reset relispartition */
19486 memset(new_val
, 0, sizeof(new_val
));
19487 memset(new_null
, false, sizeof(new_null
));
19488 memset(new_repl
, false, sizeof(new_repl
));
19489 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
19490 new_null
[Anum_pg_class_relpartbound
- 1] = true;
19491 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
19492 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
19493 new_val
, new_null
, new_repl
);
19495 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
19496 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
19497 heap_freetuple(newtuple
);
19498 table_close(classRel
, RowExclusiveLock
);
19500 if (OidIsValid(defaultPartOid
))
19503 * If the relation being detached is the default partition itself,
19504 * remove it from the parent's pg_partitioned_table entry.
19506 * If not, we must invalidate default partition's relcache entry, as
19507 * in StorePartitionBound: its partition constraint depends on every
19508 * other partition's partition constraint.
19510 if (RelationGetRelid(partRel
) == defaultPartOid
)
19511 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
19513 CacheInvalidateRelcacheByRelid(defaultPartOid
);
19517 * Invalidate the parent's relcache so that the partition is no longer
19518 * included in its partition descriptor.
19520 CacheInvalidateRelcache(rel
);
19523 * If the partition we just detached is partitioned itself, invalidate
19524 * relcache for all descendent partitions too to ensure that their
19525 * rd_partcheck expression trees are rebuilt; must lock partitions before
19526 * doing so, using the same lockmode as what partRel has been locked with
19529 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19533 children
= find_all_inheritors(RelationGetRelid(partRel
),
19534 AccessExclusiveLock
, NULL
);
19535 foreach(cell
, children
)
19537 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
19543 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19545 * To use when a DETACH PARTITION command previously did not run to
19546 * completion; this completes the detaching process.
19548 static ObjectAddress
19549 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
19552 ObjectAddress address
;
19553 Snapshot snap
= GetActiveSnapshot();
19555 partRel
= table_openrv(name
, AccessExclusiveLock
);
19558 * Wait until existing snapshots are gone. This is important if the
19559 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19560 * user could immediately run DETACH FINALIZE without actually waiting for
19561 * existing transactions. We must not complete the detach action until
19562 * all such queries are complete (otherwise we would present them with an
19563 * inconsistent view of catalogs).
19565 WaitForOlderSnapshots(snap
->xmin
, false);
19567 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
19569 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19571 table_close(partRel
, NoLock
);
19577 * DetachAddConstraintIfNeeded
19578 * Subroutine for ATExecDetachPartition. Create a constraint that
19579 * takes the place of the partition constraint, but avoid creating
19580 * a dupe if an constraint already exists which implies the needed
19584 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
19586 List
*constraintExpr
;
19588 constraintExpr
= RelationGetPartitionQual(partRel
);
19589 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
19592 * Avoid adding a new constraint if the needed constraint is implied by an
19593 * existing constraint
19595 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
19597 AlteredTableInfo
*tab
;
19600 tab
= ATGetQueueEntry(wqueue
, partRel
);
19602 /* Add constraint on partition, equivalent to the partition constraint */
19603 n
= makeNode(Constraint
);
19604 n
->contype
= CONSTR_CHECK
;
19607 n
->is_no_inherit
= false;
19608 n
->raw_expr
= NULL
;
19609 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
19610 n
->initially_valid
= true;
19611 n
->skip_validation
= true;
19612 /* It's a re-add, since it nominally already exists */
19613 ATAddCheckNNConstraint(wqueue
, tab
, partRel
, n
,
19614 true, false, true, ShareUpdateExclusiveLock
);
19619 * DropClonedTriggersFromPartition
19620 * subroutine for ATExecDetachPartition to remove any triggers that were
19621 * cloned to the partition when it was created-as-partition or attached.
19622 * This undoes what CloneRowTriggersToPartition did.
19625 DropClonedTriggersFromPartition(Oid partitionId
)
19631 ObjectAddresses
*objects
;
19633 objects
= new_object_addresses();
19636 * Scan pg_trigger to search for all triggers on this rel.
19638 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19639 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
19640 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19641 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
19642 true, NULL
, 1, &skey
);
19643 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
19645 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
19646 ObjectAddress trig
;
19648 /* Ignore triggers that weren't cloned */
19649 if (!OidIsValid(pg_trigger
->tgparentid
))
19653 * Ignore internal triggers that are implementation objects of foreign
19654 * keys, because these will be detached when the foreign keys
19657 if (OidIsValid(pg_trigger
->tgconstrrelid
))
19661 * This is ugly, but necessary: remove the dependency markings on the
19662 * trigger so that it can be removed.
19664 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
19666 DEPENDENCY_PARTITION_PRI
);
19667 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
19668 RelationRelationId
,
19669 DEPENDENCY_PARTITION_SEC
);
19671 /* remember this trigger to remove it below */
19672 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
19673 add_exact_object_address(&trig
, objects
);
19676 /* make the dependency removal visible to the deletion below */
19677 CommandCounterIncrement();
19678 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
19681 free_object_addresses(objects
);
19682 systable_endscan(scan
);
19683 table_close(tgrel
, RowExclusiveLock
);
19687 * Before acquiring lock on an index, acquire the same lock on the owning
19690 struct AttachIndexCallbackState
19694 bool lockedParentTbl
;
19698 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
19701 struct AttachIndexCallbackState
*state
;
19702 Form_pg_class classform
;
19705 state
= (struct AttachIndexCallbackState
*) arg
;
19707 if (!state
->lockedParentTbl
)
19709 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
19710 state
->lockedParentTbl
= true;
19714 * If we previously locked some other heap, and the name we're looking up
19715 * no longer refers to an index on that relation, release the now-useless
19716 * lock. XXX maybe we should do *after* we verify whether the index does
19717 * not actually belong to the same relation ...
19719 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
19721 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
19722 state
->partitionOid
= InvalidOid
;
19725 /* Didn't find a relation, so no need for locking or permission checks. */
19726 if (!OidIsValid(relOid
))
19729 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
19730 if (!HeapTupleIsValid(tuple
))
19731 return; /* concurrently dropped, so nothing to do */
19732 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
19733 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
19734 classform
->relkind
!= RELKIND_INDEX
)
19736 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19737 errmsg("\"%s\" is not an index", rv
->relname
)));
19738 ReleaseSysCache(tuple
);
19741 * Since we need only examine the heap's tupledesc, an access share lock
19742 * on it (preventing any DDL) is sufficient.
19744 state
->partitionOid
= IndexGetRelation(relOid
, false);
19745 LockRelationOid(state
->partitionOid
, AccessShareLock
);
19749 * ALTER INDEX i1 ATTACH PARTITION i2
19751 static ObjectAddress
19752 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
19756 Relation parentTbl
;
19757 ObjectAddress address
;
19760 struct AttachIndexCallbackState state
;
19763 * We need to obtain lock on the index 'name' to modify it, but we also
19764 * need to read its owning table's tuple descriptor -- so we need to lock
19765 * both. To avoid deadlocks, obtain lock on the table before doing so on
19766 * the index. Furthermore, we need to examine the parent table of the
19767 * partition, so lock that one too.
19769 state
.partitionOid
= InvalidOid
;
19770 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
19771 state
.lockedParentTbl
= false;
19773 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
19774 RangeVarCallbackForAttachIndex
,
19777 if (!OidIsValid(partIdxId
))
19779 (errcode(ERRCODE_UNDEFINED_OBJECT
),
19780 errmsg("index \"%s\" does not exist", name
->relname
)));
19782 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19783 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
19785 /* we already hold locks on both tables, so this is safe: */
19786 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
19787 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
19789 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
19791 /* Silently do nothing if already in the right state */
19792 currParent
= partIdx
->rd_rel
->relispartition
?
19793 get_partition_parent(partIdxId
, false) : InvalidOid
;
19794 if (currParent
!= RelationGetRelid(parentIdx
))
19796 IndexInfo
*childInfo
;
19797 IndexInfo
*parentInfo
;
19801 PartitionDesc partDesc
;
19803 cldConstrId
= InvalidOid
;
19806 * If this partition already has an index attached, refuse the
19809 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
19811 if (OidIsValid(currParent
))
19813 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19814 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19815 RelationGetRelationName(partIdx
),
19816 RelationGetRelationName(parentIdx
)),
19817 errdetail("Index \"%s\" is already attached to another index.",
19818 RelationGetRelationName(partIdx
))));
19820 /* Make sure it indexes a partition of the other index's table */
19821 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
19823 for (i
= 0; i
< partDesc
->nparts
; i
++)
19825 if (partDesc
->oids
[i
] == state
.partitionOid
)
19833 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19834 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19835 RelationGetRelationName(partIdx
),
19836 RelationGetRelationName(parentIdx
)),
19837 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19838 RelationGetRelationName(partIdx
),
19839 RelationGetRelationName(parentTbl
))));
19841 /* Ensure the indexes are compatible */
19842 childInfo
= BuildIndexInfo(partIdx
);
19843 parentInfo
= BuildIndexInfo(parentIdx
);
19844 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
19845 RelationGetDescr(parentTbl
),
19847 if (!CompareIndexInfo(childInfo
, parentInfo
,
19848 partIdx
->rd_indcollation
,
19849 parentIdx
->rd_indcollation
,
19850 partIdx
->rd_opfamily
,
19851 parentIdx
->rd_opfamily
,
19854 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19855 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19856 RelationGetRelationName(partIdx
),
19857 RelationGetRelationName(parentIdx
)),
19858 errdetail("The index definitions do not match.")));
19861 * If there is a constraint in the parent, make sure there is one in
19864 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
19865 RelationGetRelid(parentIdx
));
19867 if (OidIsValid(constraintOid
))
19869 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
19871 if (!OidIsValid(cldConstrId
))
19873 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19874 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19875 RelationGetRelationName(partIdx
),
19876 RelationGetRelationName(parentIdx
)),
19877 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19878 RelationGetRelationName(parentIdx
),
19879 RelationGetRelationName(parentTbl
),
19880 RelationGetRelationName(partIdx
))));
19884 * If it's a primary key, make sure the columns in the partition are
19887 if (parentIdx
->rd_index
->indisprimary
)
19888 verifyPartitionIndexNotNull(childInfo
, partTbl
);
19890 /* All good -- do it */
19891 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
19892 if (OidIsValid(constraintOid
))
19893 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
19894 RelationGetRelid(partTbl
));
19896 free_attrmap(attmap
);
19898 validatePartitionedIndex(parentIdx
, parentTbl
);
19901 relation_close(parentTbl
, AccessShareLock
);
19902 /* keep these locks till commit */
19903 relation_close(partTbl
, NoLock
);
19904 relation_close(partIdx
, NoLock
);
19910 * Verify whether the given partition already contains an index attached
19911 * to the given partitioned index. If so, raise an error.
19914 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
19918 existingIdx
= index_get_partition(partitionTbl
,
19919 RelationGetRelid(parentIdx
));
19920 if (OidIsValid(existingIdx
))
19922 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19923 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19924 RelationGetRelationName(partIdx
),
19925 RelationGetRelationName(parentIdx
)),
19926 errdetail("Another index is already attached for partition \"%s\".",
19927 RelationGetRelationName(partitionTbl
))));
19931 * Verify whether the set of attached partition indexes to a parent index on
19932 * a partitioned table is complete. If it is, mark the parent index valid.
19934 * This should be called each time a partition index is attached.
19937 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
19939 Relation inheritsRel
;
19944 bool updated
= false;
19946 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
19949 * Scan pg_inherits for this parent index. Count each valid index we find
19950 * (verifying the pg_index entry for each), and if we reach the total
19951 * amount we expect, we can mark this parent index as valid.
19953 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
19954 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
19955 BTEqualStrategyNumber
, F_OIDEQ
,
19956 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19957 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
19959 while ((inhTup
= systable_getnext(scan
)) != NULL
)
19961 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
19963 Form_pg_index indexForm
;
19965 indTup
= SearchSysCache1(INDEXRELID
,
19966 ObjectIdGetDatum(inhForm
->inhrelid
));
19967 if (!HeapTupleIsValid(indTup
))
19968 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
19969 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19970 if (indexForm
->indisvalid
)
19972 ReleaseSysCache(indTup
);
19975 /* Done with pg_inherits */
19976 systable_endscan(scan
);
19977 table_close(inheritsRel
, AccessShareLock
);
19980 * If we found as many inherited indexes as the partitioned table has
19981 * partitions, we're good; update pg_index to set indisvalid.
19983 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
19987 Form_pg_index indexForm
;
19989 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
19990 indTup
= SearchSysCacheCopy1(INDEXRELID
,
19991 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19992 if (!HeapTupleIsValid(indTup
))
19993 elog(ERROR
, "cache lookup failed for index %u",
19994 RelationGetRelid(partedIdx
));
19995 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19997 indexForm
->indisvalid
= true;
20000 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
20002 table_close(idxRel
, RowExclusiveLock
);
20003 heap_freetuple(indTup
);
20007 * If this index is in turn a partition of a larger index, validating it
20008 * might cause the parent to become valid also. Try that.
20010 if (updated
&& partedIdx
->rd_rel
->relispartition
)
20014 Relation parentIdx
,
20017 /* make sure we see the validation we just did */
20018 CommandCounterIncrement();
20020 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
20021 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
20022 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
20023 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
20024 Assert(!parentIdx
->rd_index
->indisvalid
);
20026 validatePartitionedIndex(parentIdx
, parentTbl
);
20028 relation_close(parentIdx
, AccessExclusiveLock
);
20029 relation_close(parentTbl
, AccessExclusiveLock
);
20034 * When attaching an index as a partition of a partitioned index which is a
20035 * primary key, verify that all the columns in the partition are marked NOT
20039 verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partition
)
20041 for (int i
= 0; i
< iinfo
->ii_NumIndexKeyAttrs
; i
++)
20043 Form_pg_attribute att
= TupleDescAttr(RelationGetDescr(partition
),
20044 iinfo
->ii_IndexAttrNumbers
[i
] - 1);
20046 if (!att
->attnotnull
)
20048 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
20049 errmsg("invalid primary key definition"),
20050 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20051 NameStr(att
->attname
),
20052 RelationGetRelationName(partition
)));
20057 * Return an OID list of constraints that reference the given relation
20058 * that are marked as having a parent constraints.
20061 GetParentedForeignKeyRefs(Relation partition
)
20063 Relation pg_constraint
;
20066 ScanKeyData key
[2];
20067 List
*constraints
= NIL
;
20070 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20073 if (RelationGetIndexList(partition
) == NIL
||
20074 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
20075 INDEX_ATTR_BITMAP_KEY
)))
20078 /* Search for constraints referencing this table */
20079 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
20080 ScanKeyInit(&key
[0],
20081 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
20082 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
20083 ScanKeyInit(&key
[1],
20084 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
20085 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
20087 /* XXX This is a seqscan, as we don't have a usable index */
20088 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
20089 while ((tuple
= systable_getnext(scan
)) != NULL
)
20091 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20094 * We only need to process constraints that are part of larger ones.
20096 if (!OidIsValid(constrForm
->conparentid
))
20099 constraints
= lappend_oid(constraints
, constrForm
->oid
);
20102 systable_endscan(scan
);
20103 table_close(pg_constraint
, AccessShareLock
);
20105 return constraints
;
20109 * During DETACH PARTITION, verify that any foreign keys pointing to the
20110 * partitioned table would not become invalid. An error is raised if any
20111 * referenced values exist.
20114 ATDetachCheckNoForeignKeyRefs(Relation partition
)
20119 constraints
= GetParentedForeignKeyRefs(partition
);
20121 foreach(cell
, constraints
)
20123 Oid constrOid
= lfirst_oid(cell
);
20125 Form_pg_constraint constrForm
;
20127 Trigger trig
= {0};
20129 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
20130 if (!HeapTupleIsValid(tuple
))
20131 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
20132 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20134 Assert(OidIsValid(constrForm
->conparentid
));
20135 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
20137 /* prevent data changes into the referencing table until commit */
20138 rel
= table_open(constrForm
->conrelid
, ShareLock
);
20140 trig
.tgoid
= InvalidOid
;
20141 trig
.tgname
= NameStr(constrForm
->conname
);
20142 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
20143 trig
.tgisinternal
= true;
20144 trig
.tgconstrrelid
= RelationGetRelid(partition
);
20145 trig
.tgconstrindid
= constrForm
->conindid
;
20146 trig
.tgconstraint
= constrForm
->oid
;
20147 trig
.tgdeferrable
= false;
20148 trig
.tginitdeferred
= false;
20149 /* we needn't fill in remaining fields */
20151 RI_PartitionRemove_Check(&trig
, rel
, partition
);
20153 ReleaseSysCache(tuple
);
20155 table_close(rel
, NoLock
);
20160 * resolve column compression specification to compression method.
20163 GetAttributeCompression(Oid atttypid
, const char *compression
)
20167 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
20168 return InvalidCompressionMethod
;
20171 * To specify a nondefault method, the column data type must be toastable.
20172 * Note this says nothing about whether the column's attstorage setting
20173 * permits compression; we intentionally allow attstorage and
20174 * attcompression to be independent. But with a non-toastable type,
20175 * attstorage could not be set to a value that would permit compression.
20177 * We don't actually need to enforce this, since nothing bad would happen
20178 * if attcompression were non-default; it would never be consulted. But
20179 * it seems more user-friendly to complain about a certainly-useless
20180 * attempt to set the property.
20182 if (!TypeIsToastable(atttypid
))
20184 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20185 errmsg("column data type %s does not support compression",
20186 format_type_be(atttypid
))));
20188 cmethod
= CompressionNameToMethod(compression
);
20189 if (!CompressionMethodIsValid(cmethod
))
20191 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20192 errmsg("invalid compression method \"%s\"", compression
)));
20198 * resolve column storage specification
20201 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
20205 if (pg_strcasecmp(storagemode
, "plain") == 0)
20206 cstorage
= TYPSTORAGE_PLAIN
;
20207 else if (pg_strcasecmp(storagemode
, "external") == 0)
20208 cstorage
= TYPSTORAGE_EXTERNAL
;
20209 else if (pg_strcasecmp(storagemode
, "extended") == 0)
20210 cstorage
= TYPSTORAGE_EXTENDED
;
20211 else if (pg_strcasecmp(storagemode
, "main") == 0)
20212 cstorage
= TYPSTORAGE_MAIN
;
20213 else if (pg_strcasecmp(storagemode
, "default") == 0)
20214 cstorage
= get_typstorage(atttypid
);
20217 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20218 errmsg("invalid storage type \"%s\"",
20222 * safety check: do not allow toasted storage modes unless column datatype
20225 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
20227 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20228 errmsg("column data type %s can only have storage PLAIN",
20229 format_type_be(atttypid
))));