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
*schema
, List
*supers
, char relpersistence
,
354 bool is_partition
, List
**supconstr
);
355 static bool MergeCheckConstraint(List
*constraints
, char *name
, Node
*expr
);
356 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
);
357 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
358 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
359 bool child_is_partition
);
360 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
361 int32 seqNumber
, Relation inhRelation
,
362 bool child_is_partition
);
363 static int findAttrByName(const char *attributeName
, List
*schema
);
364 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
365 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
366 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
367 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
369 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
370 bool recurse
, bool recursing
, LOCKMODE lockmode
);
371 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
372 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
374 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
375 Relation rel
, char *constrName
,
376 bool recurse
, bool recursing
, LOCKMODE lockmode
);
377 static int transformColumnNameList(Oid relId
, List
*colList
,
378 int16
*attnums
, Oid
*atttypids
);
379 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
381 int16
*attnums
, Oid
*atttypids
,
383 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
384 int numattrs
, int16
*attnums
,
386 static void checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
);
387 static CoercionPathType
findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
,
389 static void validateForeignKeyConstraint(char *conname
,
390 Relation rel
, Relation pkrel
,
391 Oid pkindOid
, Oid constraintOid
);
392 static void ATController(AlterTableStmt
*parsetree
,
393 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
394 AlterTableUtilityContext
*context
);
395 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
396 bool recurse
, bool recursing
, LOCKMODE lockmode
,
397 AlterTableUtilityContext
*context
);
398 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
399 AlterTableUtilityContext
*context
);
400 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
401 AlterTableCmd
*cmd
, LOCKMODE lockmode
, int cur_pass
,
402 AlterTableUtilityContext
*context
);
403 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
404 Relation rel
, AlterTableCmd
*cmd
,
405 bool recurse
, LOCKMODE lockmode
,
407 AlterTableUtilityContext
*context
);
408 static void ATRewriteTables(AlterTableStmt
*parsetree
,
409 List
**wqueue
, LOCKMODE lockmode
,
410 AlterTableUtilityContext
*context
);
411 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
);
412 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
413 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
414 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
415 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
416 AlterTableUtilityContext
*context
);
417 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
418 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
420 AlterTableUtilityContext
*context
);
421 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
422 DropBehavior behavior
);
423 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
424 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
425 AlterTableUtilityContext
*context
);
426 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
427 Relation rel
, AlterTableCmd
**cmd
,
428 bool recurse
, bool recursing
,
429 LOCKMODE lockmode
, int cur_pass
,
430 AlterTableUtilityContext
*context
);
431 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
433 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
434 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
435 static void ATPrepDropNotNull(Relation rel
, bool recurse
, bool recursing
);
436 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, LOCKMODE lockmode
);
437 static void ATPrepSetNotNull(List
**wqueue
, Relation rel
,
438 AlterTableCmd
*cmd
, bool recurse
, bool recursing
,
440 AlterTableUtilityContext
*context
);
441 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo
*tab
, Relation rel
,
442 const char *colName
, LOCKMODE lockmode
);
443 static void ATExecCheckNotNull(AlteredTableInfo
*tab
, Relation rel
,
444 const char *colName
, LOCKMODE lockmode
);
445 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
446 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
447 List
*testConstraint
, List
*provenConstraint
);
448 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
449 Node
*newDefault
, LOCKMODE lockmode
);
450 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
452 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
453 Node
*def
, LOCKMODE lockmode
);
454 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
455 Node
*def
, LOCKMODE lockmode
);
456 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
457 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
458 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
459 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
460 Node
*newValue
, LOCKMODE lockmode
);
461 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
462 Node
*options
, bool isReset
, LOCKMODE lockmode
);
463 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
464 Node
*newValue
, LOCKMODE lockmode
);
465 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
466 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
467 AlterTableUtilityContext
*context
);
468 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
469 DropBehavior behavior
,
470 bool recurse
, bool recursing
,
471 bool missing_ok
, LOCKMODE lockmode
,
472 ObjectAddresses
*addrs
);
473 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
474 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
475 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
476 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
477 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
478 AlteredTableInfo
*tab
, Relation rel
,
479 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
481 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
482 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
483 IndexStmt
*stmt
, LOCKMODE lockmode
);
484 static ObjectAddress
ATAddCheckConstraint(List
**wqueue
,
485 AlteredTableInfo
*tab
, Relation rel
,
487 bool recurse
, bool recursing
, bool is_readd
,
489 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
490 Relation rel
, Constraint
*fkconstraint
,
491 bool recurse
, bool recursing
,
493 static ObjectAddress
addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
,
494 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
495 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
496 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
497 int numfkdelsetcols
, int16
*fkdelsetcols
,
499 Oid parentDelTrigger
, Oid parentUpdTrigger
);
500 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
501 int numfksetcols
, const int16
*fksetcolsattnums
,
503 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
504 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
505 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
506 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
507 int numfkdelsetcols
, int16
*fkdelsetcols
,
508 bool old_check_ok
, LOCKMODE lockmode
,
509 Oid parentInsTrigger
, Oid parentUpdTrigger
);
510 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
511 Relation partitionRel
);
512 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
513 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
515 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
516 Constraint
*fkconstraint
, Oid constraintOid
,
518 Oid parentInsTrigger
, Oid parentUpdTrigger
,
519 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
520 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
521 Constraint
*fkconstraint
, Oid constraintOid
,
523 Oid parentDelTrigger
, Oid parentUpdTrigger
,
524 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
525 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
527 Oid parentConstrOid
, int numfks
,
528 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
530 Oid parentInsTrigger
,
531 Oid parentUpdTrigger
,
533 static void GetForeignKeyActionTriggers(Relation trigrel
,
534 Oid conoid
, Oid confrelid
, Oid conrelid
,
535 Oid
*deleteTriggerOid
,
536 Oid
*updateTriggerOid
);
537 static void GetForeignKeyCheckTriggers(Relation trigrel
,
538 Oid conoid
, Oid confrelid
, Oid conrelid
,
539 Oid
*insertTriggerOid
,
540 Oid
*updateTriggerOid
);
541 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
542 DropBehavior behavior
,
543 bool recurse
, bool recursing
,
544 bool missing_ok
, LOCKMODE lockmode
);
545 static void ATPrepAlterColumnType(List
**wqueue
,
546 AlteredTableInfo
*tab
, Relation rel
,
547 bool recurse
, bool recursing
,
548 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
549 AlterTableUtilityContext
*context
);
550 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
551 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
552 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
553 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
554 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
555 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
556 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
558 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
559 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
561 static void RebuildConstraintComment(AlteredTableInfo
*tab
, int pass
,
562 Oid objid
, Relation rel
, List
*domname
,
563 const char *conname
);
564 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
565 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
566 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
567 List
*options
, LOCKMODE lockmode
);
568 static void change_owner_fix_column_acls(Oid relationOid
,
569 Oid oldOwnerId
, Oid newOwnerId
);
570 static void change_owner_recurse_to_sequences(Oid relationOid
,
571 Oid newOwnerId
, LOCKMODE lockmode
);
572 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
574 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
575 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
576 static bool ATPrepChangePersistence(Relation rel
, bool toLogged
);
577 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
578 const char *tablespacename
, LOCKMODE lockmode
);
579 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
580 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
581 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
582 AlterTableType operation
,
584 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
585 char fires_when
, bool skip_system
, bool recurse
,
587 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
588 char fires_when
, LOCKMODE lockmode
);
589 static void ATPrepAddInherit(Relation child_rel
);
590 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
591 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
592 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
593 DependencyType deptype
);
594 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
595 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
596 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
597 static void ATExecGenericOptions(Relation rel
, List
*options
);
598 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
599 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
600 static ObjectAddress
ATExecSetCompression(Relation rel
,
601 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
603 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
604 static const char *storage_name(char c
);
606 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
607 Oid oldRelOid
, void *arg
);
608 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
609 Oid oldrelid
, void *arg
);
610 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
611 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
612 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
613 PartitionStrategy strategy
);
614 static void CreateInheritance(Relation child_rel
, Relation parent_rel
);
615 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
616 bool expect_detached
);
617 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
619 AlterTableUtilityContext
*context
);
620 static void AttachPartitionEnsureIndexes(Relation rel
, Relation attachrel
);
621 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
622 List
*partConstraint
,
623 bool validate_default
);
624 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
625 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
626 static void DropClonedTriggersFromPartition(Oid partitionId
);
627 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
628 Relation rel
, RangeVar
*name
,
630 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
631 bool concurrent
, Oid defaultPartOid
);
632 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
633 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
635 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
636 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
637 Relation partitionTbl
);
638 static List
*GetParentedForeignKeyRefs(Relation partition
);
639 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
640 static char GetAttributeCompression(Oid atttypid
, char *compression
);
641 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
644 /* ----------------------------------------------------------------
646 * Creates a new relation.
648 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
649 * The other arguments are used to extend the behavior for other cases:
650 * relkind: relkind to assign to the new relation
651 * ownerId: if not InvalidOid, use this as the new relation's owner.
652 * typaddress: if not null, it's set to the pg_type entry's address.
653 * queryString: for error reporting
655 * Note that permissions checks are done against current user regardless of
656 * ownerId. A nonzero ownerId is used when someone is creating a relation
657 * "on behalf of" someone else, so we still want to see that the current user
658 * has permissions to do it.
660 * If successful, returns the address of the new relation.
661 * ----------------------------------------------------------------
664 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
665 ObjectAddress
*typaddress
, const char *queryString
)
667 char relname
[NAMEDATALEN
];
672 TupleDesc descriptor
;
674 List
*old_constraints
;
676 List
*cookedDefaults
;
681 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
683 ObjectAddress address
;
684 LOCKMODE parentLockmode
;
685 const char *accessMethod
= NULL
;
686 Oid accessMethodId
= InvalidOid
;
689 * Truncate relname to appropriate length (probably a waste of time, as
690 * parser should have done this already).
692 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
695 * Check consistency of arguments
697 if (stmt
->oncommit
!= ONCOMMIT_NOOP
698 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
700 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
701 errmsg("ON COMMIT can only be used on temporary tables")));
703 if (stmt
->partspec
!= NULL
)
705 if (relkind
!= RELKIND_RELATION
)
706 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
708 relkind
= RELKIND_PARTITIONED_TABLE
;
715 * Look up the namespace in which we are supposed to create the relation,
716 * check we have permission to create there, lock it against concurrent
717 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
718 * namespace is selected.
721 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
724 * Security check: disallow creating temp tables from security-restricted
725 * code. This is needed because calling code might not expect untrusted
726 * tables to appear in pg_temp at the front of its search path.
728 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
729 && InSecurityRestrictedOperation())
731 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
732 errmsg("cannot create temporary table within security-restricted operation")));
735 * Determine the lockmode to use when scanning parents. A self-exclusive
736 * lock is needed here.
738 * For regular inheritance, if two backends attempt to add children to the
739 * same parent simultaneously, and that parent has no pre-existing
740 * children, then both will attempt to update the parent's relhassubclass
741 * field, leading to a "tuple concurrently updated" error. Also, this
742 * interlocks against a concurrent ANALYZE on the parent table, which
743 * might otherwise be attempting to clear the parent's relhassubclass
744 * field, if its previous children were recently dropped.
746 * If the child table is a partition, then we instead grab an exclusive
747 * lock on the parent because its partition descriptor will be changed by
748 * addition of the new partition.
750 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
751 ShareUpdateExclusiveLock
);
753 /* Determine the list of OIDs of the parents. */
755 foreach(listptr
, stmt
->inhRelations
)
757 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
760 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
763 * Reject duplications in the list of parents.
765 if (list_member_oid(inheritOids
, parentOid
))
767 (errcode(ERRCODE_DUPLICATE_TABLE
),
768 errmsg("relation \"%s\" would be inherited from more than once",
769 get_rel_name(parentOid
))));
771 inheritOids
= lappend_oid(inheritOids
, parentOid
);
775 * Select tablespace to use: an explicitly indicated one, or (in the case
776 * of a partitioned table) the parent's, if it has one.
778 if (stmt
->tablespacename
)
780 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
782 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
784 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
785 errmsg("cannot specify default tablespace for partitioned relations")));
787 else if (stmt
->partbound
)
790 * For partitions, when no other tablespace is specified, we default
791 * the tablespace to the parent partitioned table's.
793 Assert(list_length(inheritOids
) == 1);
794 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
797 tablespaceId
= InvalidOid
;
799 /* still nothing? use the default */
800 if (!OidIsValid(tablespaceId
))
801 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
804 /* Check permissions except when using database's default */
805 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
809 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
811 if (aclresult
!= ACLCHECK_OK
)
812 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
813 get_tablespace_name(tablespaceId
));
816 /* In all cases disallow placing user relations in pg_global */
817 if (tablespaceId
== GLOBALTABLESPACE_OID
)
819 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
820 errmsg("only shared relations can be placed in pg_global tablespace")));
822 /* Identify user ID that will own the table */
823 if (!OidIsValid(ownerId
))
824 ownerId
= GetUserId();
827 * Parse and validate reloptions, if any.
829 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
835 (void) view_reloptions(reloptions
, true);
837 case RELKIND_PARTITIONED_TABLE
:
838 (void) partitioned_table_reloptions(reloptions
, true);
841 (void) heap_reloptions(relkind
, reloptions
, true);
844 if (stmt
->ofTypename
)
848 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
850 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
851 if (aclresult
!= ACLCHECK_OK
)
852 aclcheck_error_type(aclresult
, ofTypeId
);
855 ofTypeId
= InvalidOid
;
858 * Look up inheritance ancestors and generate relation schema, including
859 * inherited attributes. (Note that stmt->tableElts is destructively
860 * modified by MergeAttributes.)
863 MergeAttributes(stmt
->tableElts
, inheritOids
,
864 stmt
->relation
->relpersistence
,
865 stmt
->partbound
!= NULL
,
869 * Create a tuple descriptor from the relation schema. Note that this
870 * deals with column names, types, and NOT NULL constraints, but not
871 * default values or CHECK constraints; we handle those below.
873 descriptor
= BuildDescForRelation(stmt
->tableElts
);
876 * Find columns with default values and prepare for insertion of the
877 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
878 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
879 * while raw defaults go into a list of RawColumnDefault structs that will
880 * be processed by AddRelationNewConstraints. (We can't deal with raw
881 * expressions until we can do transformExpr.)
883 * We can set the atthasdef flags now in the tuple descriptor; this just
884 * saves StoreAttrDefault from having to do an immediate update of the
888 cookedDefaults
= NIL
;
891 foreach(listptr
, stmt
->tableElts
)
893 ColumnDef
*colDef
= lfirst(listptr
);
894 Form_pg_attribute attr
;
897 attr
= TupleDescAttr(descriptor
, attnum
- 1);
899 if (colDef
->raw_default
!= NULL
)
901 RawColumnDefault
*rawEnt
;
903 Assert(colDef
->cooked_default
== NULL
);
905 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
906 rawEnt
->attnum
= attnum
;
907 rawEnt
->raw_default
= colDef
->raw_default
;
908 rawEnt
->missingMode
= false;
909 rawEnt
->generated
= colDef
->generated
;
910 rawDefaults
= lappend(rawDefaults
, rawEnt
);
911 attr
->atthasdef
= true;
913 else if (colDef
->cooked_default
!= NULL
)
915 CookedConstraint
*cooked
;
917 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
918 cooked
->contype
= CONSTR_DEFAULT
;
919 cooked
->conoid
= InvalidOid
; /* until created */
921 cooked
->attnum
= attnum
;
922 cooked
->expr
= colDef
->cooked_default
;
923 cooked
->skip_validation
= false;
924 cooked
->is_local
= true; /* not used for defaults */
925 cooked
->inhcount
= 0; /* ditto */
926 cooked
->is_no_inherit
= false;
927 cookedDefaults
= lappend(cookedDefaults
, cooked
);
928 attr
->atthasdef
= true;
931 if (colDef
->identity
)
932 attr
->attidentity
= colDef
->identity
;
934 if (colDef
->generated
)
935 attr
->attgenerated
= colDef
->generated
;
937 if (colDef
->compression
)
938 attr
->attcompression
= GetAttributeCompression(attr
->atttypid
,
939 colDef
->compression
);
941 if (colDef
->storage_name
)
942 attr
->attstorage
= GetAttributeStorage(attr
->atttypid
, colDef
->storage_name
);
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
);
1254 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1257 * Clean up. We keep lock on new relation (although it shouldn't be
1258 * visible to anyone else anyway, until commit).
1260 relation_close(rel
, NoLock
);
1266 * Emit the right error or warning message for a "DROP" command issued on a
1267 * non-existent relation
1270 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1272 const struct dropmsgstrings
*rentry
;
1274 if (rel
->schemaname
!= NULL
&&
1275 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1280 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1281 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1286 (errmsg("schema \"%s\" does not exist, skipping",
1292 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1294 if (rentry
->kind
== rightkind
)
1299 (errcode(rentry
->nonexistent_code
),
1300 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1304 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1310 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1314 * Emit the right error message for a "DROP" command issued on a
1315 * relation of the wrong type
1318 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1320 const struct dropmsgstrings
*rentry
;
1321 const struct dropmsgstrings
*wentry
;
1323 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1324 if (rentry
->kind
== rightkind
)
1326 Assert(rentry
->kind
!= '\0');
1328 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1329 if (wentry
->kind
== wrongkind
)
1331 /* wrongkind could be something we don't have in our table... */
1334 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1335 errmsg(rentry
->nota_msg
, relname
),
1336 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1341 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1342 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1345 RemoveRelations(DropStmt
*drop
)
1347 ObjectAddresses
*objects
;
1351 LOCKMODE lockmode
= AccessExclusiveLock
;
1353 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1354 if (drop
->concurrent
)
1357 * Note that for temporary relations this lock may get upgraded later
1358 * on, but as no other session can access a temporary relation, this
1361 lockmode
= ShareUpdateExclusiveLock
;
1362 Assert(drop
->removeType
== OBJECT_INDEX
);
1363 if (list_length(drop
->objects
) != 1)
1365 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1366 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1367 if (drop
->behavior
== DROP_CASCADE
)
1369 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1370 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1374 * First we identify all the relations, then we delete them in a single
1375 * performMultipleDeletions() call. This is to avoid unwanted DROP
1376 * RESTRICT errors if one of the relations depends on another.
1379 /* Determine required relkind */
1380 switch (drop
->removeType
)
1383 relkind
= RELKIND_RELATION
;
1387 relkind
= RELKIND_INDEX
;
1390 case OBJECT_SEQUENCE
:
1391 relkind
= RELKIND_SEQUENCE
;
1395 relkind
= RELKIND_VIEW
;
1398 case OBJECT_MATVIEW
:
1399 relkind
= RELKIND_MATVIEW
;
1402 case OBJECT_FOREIGN_TABLE
:
1403 relkind
= RELKIND_FOREIGN_TABLE
;
1407 elog(ERROR
, "unrecognized drop object type: %d",
1408 (int) drop
->removeType
);
1409 relkind
= 0; /* keep compiler quiet */
1413 /* Lock and validate each relation; build a list of object addresses */
1414 objects
= new_object_addresses();
1416 foreach(cell
, drop
->objects
)
1418 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1421 struct DropRelationCallbackState state
;
1424 * These next few steps are a great deal like relation_openrv, but we
1425 * don't bother building a relcache entry since we don't need it.
1427 * Check for shared-cache-inval messages before trying to access the
1428 * relation. This is needed to cover the case where the name
1429 * identifies a rel that has been dropped and recreated since the
1430 * start of our transaction: if we don't flush the old syscache entry,
1431 * then we'll latch onto that entry and suffer an error later.
1433 AcceptInvalidationMessages();
1435 /* Look up the appropriate relation using namespace search. */
1436 state
.expected_relkind
= relkind
;
1437 state
.heap_lockmode
= drop
->concurrent
?
1438 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1439 /* We must initialize these fields to show that no locks are held: */
1440 state
.heapOid
= InvalidOid
;
1441 state
.partParentOid
= InvalidOid
;
1443 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1444 RangeVarCallbackForDropRelation
,
1448 if (!OidIsValid(relOid
))
1450 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1455 * Decide if concurrent mode needs to be used here or not. The
1456 * callback retrieved the rel's persistence for us.
1458 if (drop
->concurrent
&&
1459 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1461 Assert(list_length(drop
->objects
) == 1 &&
1462 drop
->removeType
== OBJECT_INDEX
);
1463 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1467 * Concurrent index drop cannot be used with partitioned indexes,
1470 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1471 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1473 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1474 errmsg("cannot drop partitioned index \"%s\" concurrently",
1478 * If we're told to drop a partitioned index, we must acquire lock on
1479 * all the children of its parent partitioned table before proceeding.
1480 * Otherwise we'd try to lock the child index partitions before their
1481 * tables, leading to potential deadlock against other sessions that
1482 * will lock those objects in the other order.
1484 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1485 (void) find_all_inheritors(state
.heapOid
,
1486 state
.heap_lockmode
,
1489 /* OK, we're ready to delete this one */
1490 obj
.classId
= RelationRelationId
;
1491 obj
.objectId
= relOid
;
1492 obj
.objectSubId
= 0;
1494 add_exact_object_address(&obj
, objects
);
1497 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1499 free_object_addresses(objects
);
1503 * Before acquiring a table lock, check whether we have sufficient rights.
1504 * In the case of DROP INDEX, also try to lock the table before the index.
1505 * Also, if the table to be dropped is a partition, we try to lock the parent
1509 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1513 struct DropRelationCallbackState
*state
;
1514 char expected_relkind
;
1516 Form_pg_class classform
;
1517 LOCKMODE heap_lockmode
;
1518 bool invalid_system_index
= false;
1520 state
= (struct DropRelationCallbackState
*) arg
;
1521 heap_lockmode
= state
->heap_lockmode
;
1524 * If we previously locked some other index's heap, and the name we're
1525 * looking up no longer refers to that relation, release the now-useless
1528 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1530 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1531 state
->heapOid
= InvalidOid
;
1535 * Similarly, if we previously locked some other partition's heap, and the
1536 * name we're looking up no longer refers to that relation, release the
1539 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1541 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1542 state
->partParentOid
= InvalidOid
;
1545 /* Didn't find a relation, so no need for locking or permission checks. */
1546 if (!OidIsValid(relOid
))
1549 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1550 if (!HeapTupleIsValid(tuple
))
1551 return; /* concurrently dropped, so nothing to do */
1552 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1553 is_partition
= classform
->relispartition
;
1555 /* Pass back some data to save lookups in RemoveRelations */
1556 state
->actual_relkind
= classform
->relkind
;
1557 state
->actual_relpersistence
= classform
->relpersistence
;
1560 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1561 * but RemoveRelations() can only pass one relkind for a given relation.
1562 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1563 * That means we must be careful before giving the wrong type error when
1564 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1565 * exists with indexes.
1567 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1568 expected_relkind
= RELKIND_RELATION
;
1569 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1570 expected_relkind
= RELKIND_INDEX
;
1572 expected_relkind
= classform
->relkind
;
1574 if (state
->expected_relkind
!= expected_relkind
)
1575 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1576 state
->expected_relkind
);
1578 /* Allow DROP to either table owner or schema owner */
1579 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1580 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1581 aclcheck_error(ACLCHECK_NOT_OWNER
,
1582 get_relkind_objtype(classform
->relkind
),
1586 * Check the case of a system index that might have been invalidated by a
1587 * failed concurrent process and allow its drop. For the time being, this
1588 * only concerns indexes of toast relations that became invalid during a
1589 * REINDEX CONCURRENTLY process.
1591 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1594 Form_pg_index indexform
;
1597 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1598 if (!HeapTupleIsValid(locTuple
))
1600 ReleaseSysCache(tuple
);
1604 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1605 indisvalid
= indexform
->indisvalid
;
1606 ReleaseSysCache(locTuple
);
1608 /* Mark object as being an invalid index of system catalogs */
1610 invalid_system_index
= true;
1613 /* In the case of an invalid index, it is fine to bypass this check */
1614 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1616 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1617 errmsg("permission denied: \"%s\" is a system catalog",
1620 ReleaseSysCache(tuple
);
1623 * In DROP INDEX, attempt to acquire lock on the parent table before
1624 * locking the index. index_drop() will need this anyway, and since
1625 * regular queries lock tables before their indexes, we risk deadlock if
1626 * we do it the other way around. No error if we don't find a pg_index
1627 * entry, though --- the relation may have been dropped. Note that this
1628 * code will execute for either plain or partitioned indexes.
1630 if (expected_relkind
== RELKIND_INDEX
&&
1631 relOid
!= oldRelOid
)
1633 state
->heapOid
= IndexGetRelation(relOid
, true);
1634 if (OidIsValid(state
->heapOid
))
1635 LockRelationOid(state
->heapOid
, heap_lockmode
);
1639 * Similarly, if the relation is a partition, we must acquire lock on its
1640 * parent before locking the partition. That's because queries lock the
1641 * parent before its partitions, so we risk deadlock if we do it the other
1644 if (is_partition
&& relOid
!= oldRelOid
)
1646 state
->partParentOid
= get_partition_parent(relOid
, true);
1647 if (OidIsValid(state
->partParentOid
))
1648 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1654 * Executes a TRUNCATE command.
1656 * This is a multi-relation truncate. We first open and grab exclusive
1657 * lock on all relations involved, checking permissions and otherwise
1658 * verifying that the relation is OK for truncation. Note that if relations
1659 * are foreign tables, at this stage, we have not yet checked that their
1660 * foreign data in external data sources are OK for truncation. These are
1661 * checked when foreign data are actually truncated later. In CASCADE mode,
1662 * relations having FK references to the targeted relations are automatically
1663 * added to the group; in RESTRICT mode, we check that all FK references are
1664 * internal to the group that's being truncated. Finally all the relations
1665 * are truncated and reindexed.
1668 ExecuteTruncate(TruncateStmt
*stmt
)
1672 List
*relids_logged
= NIL
;
1676 * Open, exclusive-lock, and check all the explicitly-specified relations
1678 foreach(cell
, stmt
->relations
)
1680 RangeVar
*rv
= lfirst(cell
);
1682 bool recurse
= rv
->inh
;
1684 LOCKMODE lockmode
= AccessExclusiveLock
;
1686 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1687 0, RangeVarCallbackForTruncate
,
1690 /* don't throw error for "TRUNCATE foo, foo" */
1691 if (list_member_oid(relids
, myrelid
))
1694 /* open the relation, we already hold a lock on it */
1695 rel
= table_open(myrelid
, NoLock
);
1698 * RangeVarGetRelidExtended() has done most checks with its callback,
1699 * but other checks with the now-opened Relation remain.
1701 truncate_check_activity(rel
);
1703 rels
= lappend(rels
, rel
);
1704 relids
= lappend_oid(relids
, myrelid
);
1706 /* Log this relation only if needed for logical decoding */
1707 if (RelationIsLogicallyLogged(rel
))
1708 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1715 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1717 foreach(child
, children
)
1719 Oid childrelid
= lfirst_oid(child
);
1721 if (list_member_oid(relids
, childrelid
))
1724 /* find_all_inheritors already got lock */
1725 rel
= table_open(childrelid
, NoLock
);
1728 * It is possible that the parent table has children that are
1729 * temp tables of other backends. We cannot safely access
1730 * such tables (because of buffering issues), and the best
1731 * thing to do is to silently ignore them. Note that this
1732 * check is the same as one of the checks done in
1733 * truncate_check_activity() called below, still it is kept
1734 * here for simplicity.
1736 if (RELATION_IS_OTHER_TEMP(rel
))
1738 table_close(rel
, lockmode
);
1743 * Inherited TRUNCATE commands perform access permission
1744 * checks on the parent table only. So we skip checking the
1745 * children's permissions and don't call
1746 * truncate_check_perms() here.
1748 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1749 truncate_check_activity(rel
);
1751 rels
= lappend(rels
, rel
);
1752 relids
= lappend_oid(relids
, childrelid
);
1754 /* Log this relation only if needed for logical decoding */
1755 if (RelationIsLogicallyLogged(rel
))
1756 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1759 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1761 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1762 errmsg("cannot truncate only a partitioned table"),
1763 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1766 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1767 stmt
->behavior
, stmt
->restart_seqs
, false);
1769 /* And close the rels */
1772 Relation rel
= (Relation
) lfirst(cell
);
1774 table_close(rel
, NoLock
);
1779 * ExecuteTruncateGuts
1781 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1782 * command (see above) as well as replication subscribers that execute a
1783 * replicated TRUNCATE action.
1785 * explicit_rels is the list of Relations to truncate that the command
1786 * specified. relids is the list of Oids corresponding to explicit_rels.
1787 * relids_logged is the list of Oids (a subset of relids) that require
1788 * WAL-logging. This is all a bit redundant, but the existing callers have
1789 * this information handy in this form.
1792 ExecuteTruncateGuts(List
*explicit_rels
,
1794 List
*relids_logged
,
1795 DropBehavior behavior
, bool restart_seqs
,
1796 bool run_as_table_owner
)
1799 List
*seq_relids
= NIL
;
1800 HTAB
*ft_htab
= NULL
;
1802 ResultRelInfo
*resultRelInfos
;
1803 ResultRelInfo
*resultRelInfo
;
1804 SubTransactionId mySubid
;
1809 * Check the explicitly-specified relations.
1811 * In CASCADE mode, suck in all referencing relations as well. This
1812 * requires multiple iterations to find indirectly-dependent relations. At
1813 * each phase, we need to exclusive-lock new rels before looking for their
1814 * dependencies, else we might miss something. Also, we check each rel as
1815 * soon as we open it, to avoid a faux pas such as holding lock for a long
1816 * time on a rel we have no permissions for.
1818 rels
= list_copy(explicit_rels
);
1819 if (behavior
== DROP_CASCADE
)
1825 newrelids
= heap_truncate_find_FKs(relids
);
1826 if (newrelids
== NIL
)
1827 break; /* nothing else to add */
1829 foreach(cell
, newrelids
)
1831 Oid relid
= lfirst_oid(cell
);
1834 rel
= table_open(relid
, AccessExclusiveLock
);
1836 (errmsg("truncate cascades to table \"%s\"",
1837 RelationGetRelationName(rel
))));
1838 truncate_check_rel(relid
, rel
->rd_rel
);
1839 truncate_check_perms(relid
, rel
->rd_rel
);
1840 truncate_check_activity(rel
);
1841 rels
= lappend(rels
, rel
);
1842 relids
= lappend_oid(relids
, relid
);
1844 /* Log this relation only if needed for logical decoding */
1845 if (RelationIsLogicallyLogged(rel
))
1846 relids_logged
= lappend_oid(relids_logged
, relid
);
1852 * Check foreign key references. In CASCADE mode, this should be
1853 * unnecessary since we just pulled in all the references; but as a
1854 * cross-check, do it anyway if in an Assert-enabled build.
1856 #ifdef USE_ASSERT_CHECKING
1857 heap_truncate_check_FKs(rels
, false);
1859 if (behavior
== DROP_RESTRICT
)
1860 heap_truncate_check_FKs(rels
, false);
1864 * If we are asked to restart sequences, find all the sequences, lock them
1865 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1866 * We want to do this early since it's pointless to do all the truncation
1867 * work only to fail on sequence permissions.
1873 Relation rel
= (Relation
) lfirst(cell
);
1874 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
1877 foreach(seqcell
, seqlist
)
1879 Oid seq_relid
= lfirst_oid(seqcell
);
1882 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
1884 /* This check must match AlterSequence! */
1885 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
1886 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
1887 RelationGetRelationName(seq_rel
));
1889 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
1891 relation_close(seq_rel
, NoLock
);
1896 /* Prepare to catch AFTER triggers. */
1897 AfterTriggerBeginQuery();
1900 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1901 * each relation. We don't need to call ExecOpenIndices, though.
1903 * We put the ResultRelInfos in the es_opened_result_relations list, even
1904 * though we don't have a range table and don't populate the
1905 * es_result_relations array. That's a bit bogus, but it's enough to make
1906 * ExecGetTriggerResultRel() find them.
1908 estate
= CreateExecutorState();
1909 resultRelInfos
= (ResultRelInfo
*)
1910 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
1911 resultRelInfo
= resultRelInfos
;
1914 Relation rel
= (Relation
) lfirst(cell
);
1916 InitResultRelInfo(resultRelInfo
,
1918 0, /* dummy rangetable index */
1921 estate
->es_opened_result_relations
=
1922 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
1927 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1928 * truncating (this is because one of them might throw an error). Also, if
1929 * we were to allow them to prevent statement execution, that would need
1930 * to be handled here.
1932 resultRelInfo
= resultRelInfos
;
1937 if (run_as_table_owner
)
1938 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
1940 ExecBSTruncateTriggers(estate
, resultRelInfo
);
1941 if (run_as_table_owner
)
1942 RestoreUserContext(&ucxt
);
1947 * OK, truncate each table.
1949 mySubid
= GetCurrentSubTransactionId();
1953 Relation rel
= (Relation
) lfirst(cell
);
1955 /* Skip partitioned tables as there is nothing to do */
1956 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1960 * Build the lists of foreign tables belonging to each foreign server
1961 * and pass each list to the foreign data wrapper's callback function,
1962 * so that each server can truncate its all foreign tables in bulk.
1963 * Each list is saved as a single entry in a hash table that uses the
1964 * server OID as lookup key.
1966 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1968 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
1970 ForeignTruncateInfo
*ft_info
;
1972 /* First time through, initialize hashtable for foreign tables */
1977 memset(&hctl
, 0, sizeof(HASHCTL
));
1978 hctl
.keysize
= sizeof(Oid
);
1979 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
1980 hctl
.hcxt
= CurrentMemoryContext
;
1982 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
1983 32, /* start small and extend */
1985 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
1988 /* Find or create cached entry for the foreign table */
1989 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
1992 ft_info
->serverid
= serverid
;
1993 ft_info
->rels
= NIL
;
1997 * Save the foreign table in the entry of the server that the
1998 * foreign table belongs to.
2000 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2005 * Normally, we need a transaction-safe truncation here. However, if
2006 * the table was either created in the current (sub)transaction or has
2007 * a new relfilenumber in the current (sub)transaction, then we can
2008 * just truncate it in-place, because a rollback would cause the whole
2009 * table or the current physical file to be thrown away anyway.
2011 if (rel
->rd_createSubid
== mySubid
||
2012 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2014 /* Immediate, non-rollbackable truncation is OK */
2015 heap_truncate_one_rel(rel
);
2021 ReindexParams reindex_params
= {0};
2024 * This effectively deletes all rows in the table, and may be done
2025 * in a serializable transaction. In that case we must record a
2026 * rw-conflict in to this transaction from each transaction
2027 * holding a predicate lock on the table.
2029 CheckTableForSerializableConflictIn(rel
);
2032 * Need the full transaction-safe pushups.
2034 * Create a new empty storage file for the relation, and assign it
2035 * as the relfilenumber value. The old storage file is scheduled
2036 * for deletion at commit.
2038 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2040 heap_relid
= RelationGetRelid(rel
);
2043 * The same for the toast table, if any.
2045 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2046 if (OidIsValid(toast_relid
))
2048 Relation toastrel
= relation_open(toast_relid
,
2049 AccessExclusiveLock
);
2051 RelationSetNewRelfilenumber(toastrel
,
2052 toastrel
->rd_rel
->relpersistence
);
2053 table_close(toastrel
, NoLock
);
2057 * Reconstruct the indexes to match, and we're done.
2059 reindex_relation(heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2063 pgstat_count_truncate(rel
);
2066 /* Now go through the hash table, and truncate foreign tables */
2069 ForeignTruncateInfo
*ft_info
;
2070 HASH_SEQ_STATUS seq
;
2072 hash_seq_init(&seq
, ft_htab
);
2076 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2078 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2080 /* truncate_check_rel() has checked that already */
2081 Assert(routine
->ExecForeignTruncate
!= NULL
);
2083 routine
->ExecForeignTruncate(ft_info
->rels
,
2090 hash_destroy(ft_htab
);
2096 * Restart owned sequences if we were asked to.
2098 foreach(cell
, seq_relids
)
2100 Oid seq_relid
= lfirst_oid(cell
);
2102 ResetSequence(seq_relid
);
2106 * Write a WAL record to allow this set of actions to be logically
2109 * Assemble an array of relids so we can write a single WAL record for the
2112 if (relids_logged
!= NIL
)
2114 xl_heap_truncate xlrec
;
2117 /* should only get here if wal_level >= logical */
2118 Assert(XLogLogicalInfoActive());
2120 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2121 foreach(cell
, relids_logged
)
2122 logrelids
[i
++] = lfirst_oid(cell
);
2124 xlrec
.dbId
= MyDatabaseId
;
2125 xlrec
.nrelids
= list_length(relids_logged
);
2127 if (behavior
== DROP_CASCADE
)
2128 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2130 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2133 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2134 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2136 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2138 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2142 * Process all AFTER STATEMENT TRUNCATE triggers.
2144 resultRelInfo
= resultRelInfos
;
2149 if (run_as_table_owner
)
2150 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2152 ExecASTruncateTriggers(estate
, resultRelInfo
);
2153 if (run_as_table_owner
)
2154 RestoreUserContext(&ucxt
);
2158 /* Handle queued AFTER triggers */
2159 AfterTriggerEndQuery(estate
);
2161 /* We can clean up the EState now */
2162 FreeExecutorState(estate
);
2165 * Close any rels opened by CASCADE (can't do this while EState still
2168 rels
= list_difference_ptr(rels
, explicit_rels
);
2171 Relation rel
= (Relation
) lfirst(cell
);
2173 table_close(rel
, NoLock
);
2178 * Check that a given relation is safe to truncate. Subroutine for
2179 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2182 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2184 char *relname
= NameStr(reltuple
->relname
);
2187 * Only allow truncate on regular tables, foreign tables using foreign
2188 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2189 * latter are only being included here for the following checks; no
2190 * physical truncation will occur in their case.).
2192 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2194 Oid serverid
= GetForeignServerIdByRelId(relid
);
2195 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2197 if (!fdwroutine
->ExecForeignTruncate
)
2199 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2200 errmsg("cannot truncate foreign table \"%s\"",
2203 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2204 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2206 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2207 errmsg("\"%s\" is not a table", relname
)));
2210 * Most system catalogs can't be truncated at all, or at least not unless
2211 * allow_system_table_mods=on. As an exception, however, we allow
2212 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2213 * to change its relfilenode to match the old cluster, and allowing a
2214 * TRUNCATE command to be executed is the easiest way of doing that.
2216 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2217 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2219 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2220 errmsg("permission denied: \"%s\" is a system catalog",
2223 InvokeObjectTruncateHook(relid
);
2227 * Check that current user has the permission to truncate given relation.
2230 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2232 char *relname
= NameStr(reltuple
->relname
);
2233 AclResult aclresult
;
2235 /* Permissions checks */
2236 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2237 if (aclresult
!= ACLCHECK_OK
)
2238 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2243 * Set of extra sanity checks to check if a given relation is safe to
2244 * truncate. This is split with truncate_check_rel() as
2245 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2248 truncate_check_activity(Relation rel
)
2251 * Don't allow truncate on temp tables of other backends ... their local
2252 * buffer manager is not going to cope.
2254 if (RELATION_IS_OTHER_TEMP(rel
))
2256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2257 errmsg("cannot truncate temporary tables of other sessions")));
2260 * Also check for active uses of the relation in the current transaction,
2261 * including open scans and pending AFTER trigger events.
2263 CheckTableNotInUse(rel
, "TRUNCATE");
2268 * returns the name corresponding to a typstorage/attstorage enum value
2271 storage_name(char c
)
2275 case TYPSTORAGE_PLAIN
:
2277 case TYPSTORAGE_EXTERNAL
:
2279 case TYPSTORAGE_EXTENDED
:
2281 case TYPSTORAGE_MAIN
:
2290 * Returns new schema given initial schema and superclasses.
2293 * 'schema' is the column/attribute definition for the table. (It's a list
2294 * of ColumnDef's.) It is destructively changed.
2295 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2296 * 'relpersistence' is the persistence type of the table.
2297 * 'is_partition' tells if the table is a partition.
2300 * 'supconstr' receives a list of constraints belonging to the parents,
2301 * updated as necessary to be valid for the child.
2304 * Completed schema list.
2307 * The order in which the attributes are inherited is very important.
2308 * Intuitively, the inherited attributes should come first. If a table
2309 * inherits from multiple parents, the order of those attributes are
2310 * according to the order of the parents specified in CREATE TABLE.
2312 * Here's an example:
2314 * create table person (name text, age int4, location point);
2315 * create table emp (salary int4, manager text) inherits(person);
2316 * create table student (gpa float8) inherits (person);
2317 * create table stud_emp (percent int4) inherits (emp, student);
2319 * The order of the attributes of stud_emp is:
2321 * person {1:name, 2:age, 3:location}
2323 * {6:gpa} student emp {4:salary, 5:manager}
2325 * stud_emp {7:percent}
2327 * If the same attribute name appears multiple times, then it appears
2328 * in the result table in the proper location for its first appearance.
2330 * Constraints (including NOT NULL constraints) for the child table
2331 * are the union of all relevant constraints, from both the child schema
2332 * and parent tables.
2334 * The default value for a child column is defined as:
2335 * (1) If the child schema specifies a default, that value is used.
2336 * (2) If neither the child nor any parent specifies a default, then
2337 * the column will not have a default.
2338 * (3) If conflicting defaults are inherited from different parents
2339 * (and not overridden by the child), an error is raised.
2340 * (4) Otherwise the inherited default is used.
2342 * Note that the default-value infrastructure is used for generated
2343 * columns' expressions too, so most of the preceding paragraph applies
2344 * to generation expressions too. We insist that a child column be
2345 * generated if and only if its parent(s) are, but it need not have
2346 * the same generation expression.
2350 MergeAttributes(List
*schema
, List
*supers
, char relpersistence
,
2351 bool is_partition
, List
**supconstr
)
2353 List
*inhSchema
= NIL
;
2354 List
*constraints
= NIL
;
2355 bool have_bogus_defaults
= false;
2357 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2358 List
*saved_schema
= NIL
;
2362 * Check for and reject tables with too many columns. We perform this
2363 * check relatively early for two reasons: (a) we don't run the risk of
2364 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2365 * okay if we're processing <= 1600 columns, but could take minutes to
2366 * execute if the user attempts to create a table with hundreds of
2367 * thousands of columns.
2369 * Note that we also need to check that we do not exceed this figure after
2370 * including columns from inherited relations.
2372 if (list_length(schema
) > MaxHeapAttributeNumber
)
2374 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2375 errmsg("tables can have at most %d columns",
2376 MaxHeapAttributeNumber
)));
2379 * Check for duplicate names in the explicit list of attributes.
2381 * Although we might consider merging such entries in the same way that we
2382 * handle name conflicts for inherited attributes, it seems to make more
2383 * sense to assume such conflicts are errors.
2385 * We don't use foreach() here because we have two nested loops over the
2386 * schema list, with possible element deletions in the inner one. If we
2387 * used foreach_delete_current() it could only fix up the state of one of
2388 * the loops, so it seems cleaner to use looping over list indexes for
2389 * both loops. Note that any deletion will happen beyond where the outer
2390 * loop is, so its index never needs adjustment.
2392 for (int coldefpos
= 0; coldefpos
< list_length(schema
); coldefpos
++)
2394 ColumnDef
*coldef
= list_nth_node(ColumnDef
, schema
, coldefpos
);
2396 if (!is_partition
&& coldef
->typeName
== NULL
)
2399 * Typed table column option that does not belong to a column from
2400 * the type. This works because the columns from the type come
2401 * first in the list. (We omit this check for partition column
2402 * lists; those are processed separately below.)
2405 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2406 errmsg("column \"%s\" does not exist",
2410 /* restpos scans all entries beyond coldef; incr is in loop body */
2411 for (int restpos
= coldefpos
+ 1; restpos
< list_length(schema
);)
2413 ColumnDef
*restdef
= list_nth_node(ColumnDef
, schema
, restpos
);
2415 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2417 if (coldef
->is_from_type
)
2420 * merge the column options into the column from the type
2422 coldef
->is_not_null
= restdef
->is_not_null
;
2423 coldef
->raw_default
= restdef
->raw_default
;
2424 coldef
->cooked_default
= restdef
->cooked_default
;
2425 coldef
->constraints
= restdef
->constraints
;
2426 coldef
->is_from_type
= false;
2427 schema
= list_delete_nth_cell(schema
, restpos
);
2431 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2432 errmsg("column \"%s\" specified more than once",
2441 * In case of a partition, there are no new column definitions, only dummy
2442 * ColumnDefs created for column constraints. Set them aside for now and
2443 * process them at the end.
2447 saved_schema
= schema
;
2452 * Scan the parents left-to-right, and merge their attributes to form a
2453 * list of inherited attributes (inhSchema).
2456 foreach(entry
, supers
)
2458 Oid parent
= lfirst_oid(entry
);
2460 TupleDesc tupleDesc
;
2461 TupleConstr
*constr
;
2463 List
*inherited_defaults
;
2464 List
*cols_with_defaults
;
2465 AttrNumber parent_attno
;
2469 /* caller already got lock */
2470 relation
= table_open(parent
, NoLock
);
2473 * Check for active uses of the parent partitioned table in the
2474 * current transaction, such as being used in some manner by an
2475 * enclosing command.
2478 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2481 * We do not allow partitioned tables and partitions to participate in
2482 * regular inheritance.
2484 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
2487 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2488 errmsg("cannot inherit from partitioned table \"%s\"",
2489 RelationGetRelationName(relation
))));
2490 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2492 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2493 errmsg("cannot inherit from partition \"%s\"",
2494 RelationGetRelationName(relation
))));
2496 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2497 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2498 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2500 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2501 errmsg("inherited relation \"%s\" is not a table or foreign table",
2502 RelationGetRelationName(relation
))));
2505 * If the parent is permanent, so must be all of its partitions. Note
2506 * that inheritance allows that case.
2509 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2510 relpersistence
== RELPERSISTENCE_TEMP
)
2512 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2513 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2514 RelationGetRelationName(relation
))));
2516 /* Permanent rels cannot inherit from temporary ones */
2517 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2518 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2520 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2521 errmsg(!is_partition
2522 ? "cannot inherit from temporary relation \"%s\""
2523 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2524 RelationGetRelationName(relation
))));
2526 /* If existing rel is temp, it must belong to this session */
2527 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2528 !relation
->rd_islocaltemp
)
2530 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2531 errmsg(!is_partition
2532 ? "cannot inherit from temporary relation of another session"
2533 : "cannot create as partition of temporary relation of another session")));
2536 * We should have an UNDER permission flag for this, but for now,
2537 * demand that creator of a child table own the parent.
2539 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2540 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2541 RelationGetRelationName(relation
));
2543 tupleDesc
= RelationGetDescr(relation
);
2544 constr
= tupleDesc
->constr
;
2547 * newattmap->attnums[] will contain the child-table attribute numbers
2548 * for the attributes of this parent table. (They are not the same
2549 * for parents after the first one, nor if we have dropped columns.)
2551 newattmap
= make_attrmap(tupleDesc
->natts
);
2553 /* We can't process inherited defaults until newattmap is complete. */
2554 inherited_defaults
= cols_with_defaults
= NIL
;
2556 for (parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2559 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2561 char *attributeName
= NameStr(attribute
->attname
);
2566 * Ignore dropped columns in the parent.
2568 if (attribute
->attisdropped
)
2569 continue; /* leave newattmap->attnums entry as zero */
2572 * Does it conflict with some previously inherited column?
2574 exist_attno
= findAttrByName(attributeName
, inhSchema
);
2575 if (exist_attno
> 0)
2582 * Yes, try to merge the two column definitions.
2585 (errmsg("merging multiple inherited definitions of column \"%s\"",
2587 def
= (ColumnDef
*) list_nth(inhSchema
, exist_attno
- 1);
2590 * Must have the same type and typmod
2592 typenameTypeIdAndMod(NULL
, def
->typeName
, &defTypeId
, &deftypmod
);
2593 if (defTypeId
!= attribute
->atttypid
||
2594 deftypmod
!= attribute
->atttypmod
)
2596 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2597 errmsg("inherited column \"%s\" has a type conflict",
2599 errdetail("%s versus %s",
2600 format_type_with_typemod(defTypeId
,
2602 format_type_with_typemod(attribute
->atttypid
,
2603 attribute
->atttypmod
))));
2606 * Must have the same collation
2608 defCollId
= GetColumnDefCollation(NULL
, def
, defTypeId
);
2609 if (defCollId
!= attribute
->attcollation
)
2611 (errcode(ERRCODE_COLLATION_MISMATCH
),
2612 errmsg("inherited column \"%s\" has a collation conflict",
2614 errdetail("\"%s\" versus \"%s\"",
2615 get_collation_name(defCollId
),
2616 get_collation_name(attribute
->attcollation
))));
2619 * Copy/check storage parameter
2621 if (def
->storage
== 0)
2622 def
->storage
= attribute
->attstorage
;
2623 else if (def
->storage
!= attribute
->attstorage
)
2625 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2626 errmsg("inherited column \"%s\" has a storage parameter conflict",
2628 errdetail("%s versus %s",
2629 storage_name(def
->storage
),
2630 storage_name(attribute
->attstorage
))));
2633 * Copy/check compression parameter
2635 if (CompressionMethodIsValid(attribute
->attcompression
))
2637 const char *compression
=
2638 GetCompressionMethodName(attribute
->attcompression
);
2640 if (def
->compression
== NULL
)
2641 def
->compression
= pstrdup(compression
);
2642 else if (strcmp(def
->compression
, compression
) != 0)
2644 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2645 errmsg("column \"%s\" has a compression method conflict",
2647 errdetail("%s versus %s", def
->compression
, compression
)));
2651 * Merge of NOT NULL constraints = OR 'em together
2653 def
->is_not_null
|= attribute
->attnotnull
;
2656 * Check for GENERATED conflicts
2658 if (def
->generated
!= attribute
->attgenerated
)
2660 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2661 errmsg("inherited column \"%s\" has a generation conflict",
2665 * Default and other constraints are handled below
2669 if (def
->inhcount
< 0)
2671 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
2672 errmsg("too many inheritance parents"));
2674 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2679 * No, create a new inherited column
2681 def
= makeNode(ColumnDef
);
2682 def
->colname
= pstrdup(attributeName
);
2683 def
->typeName
= makeTypeNameFromOid(attribute
->atttypid
,
2684 attribute
->atttypmod
);
2686 def
->is_local
= false;
2687 def
->is_not_null
= attribute
->attnotnull
;
2688 def
->is_from_type
= false;
2689 def
->storage
= attribute
->attstorage
;
2690 def
->raw_default
= NULL
;
2691 def
->cooked_default
= NULL
;
2692 def
->generated
= attribute
->attgenerated
;
2693 def
->collClause
= NULL
;
2694 def
->collOid
= attribute
->attcollation
;
2695 def
->constraints
= NIL
;
2697 if (CompressionMethodIsValid(attribute
->attcompression
))
2699 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2701 def
->compression
= NULL
;
2702 inhSchema
= lappend(inhSchema
, def
);
2703 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2707 * Locate default/generation expression if any
2709 if (attribute
->atthasdef
)
2711 Node
*this_default
= NULL
;
2713 /* Find default in constraint structure */
2716 AttrDefault
*attrdef
= constr
->defval
;
2718 for (int i
= 0; i
< constr
->num_defval
; i
++)
2720 if (attrdef
[i
].adnum
== parent_attno
)
2722 this_default
= stringToNode(attrdef
[i
].adbin
);
2727 if (this_default
== NULL
)
2728 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2729 parent_attno
, RelationGetRelationName(relation
));
2732 * If it's a GENERATED default, it might contain Vars that
2733 * need to be mapped to the inherited column(s)' new numbers.
2734 * We can't do that till newattmap is ready, so just remember
2735 * all the inherited default expressions for the moment.
2737 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2738 cols_with_defaults
= lappend(cols_with_defaults
, def
);
2743 * Now process any inherited default expressions, adjusting attnos
2744 * using the completed newattmap map.
2746 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2748 Node
*this_default
= (Node
*) lfirst(lc1
);
2749 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2750 bool found_whole_row
;
2752 /* Adjust Vars to match new table's column numbering */
2753 this_default
= map_variable_attnos(this_default
,
2756 InvalidOid
, &found_whole_row
);
2759 * For the moment we have to reject whole-row variables. We could
2760 * convert them, if we knew the new table's rowtype OID, but that
2761 * hasn't been assigned yet. (A variable could only appear in a
2762 * generation expression, so the error message is correct.)
2764 if (found_whole_row
)
2766 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2767 errmsg("cannot convert whole-row table reference"),
2768 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2770 RelationGetRelationName(relation
))));
2773 * If we already had a default from some prior parent, check to
2774 * see if they are the same. If so, no problem; if not, mark the
2775 * column as having a bogus default. Below, we will complain if
2776 * the bogus default isn't overridden by the child schema.
2778 Assert(def
->raw_default
== NULL
);
2779 if (def
->cooked_default
== NULL
)
2780 def
->cooked_default
= this_default
;
2781 else if (!equal(def
->cooked_default
, this_default
))
2783 def
->cooked_default
= &bogus_marker
;
2784 have_bogus_defaults
= true;
2789 * Now copy the CHECK constraints of this parent, adjusting attnos
2790 * using the completed newattmap map. Identically named constraints
2791 * are merged if possible, else we throw error.
2793 if (constr
&& constr
->num_check
> 0)
2795 ConstrCheck
*check
= constr
->check
;
2798 for (i
= 0; i
< constr
->num_check
; i
++)
2800 char *name
= check
[i
].ccname
;
2802 bool found_whole_row
;
2804 /* ignore if the constraint is non-inheritable */
2805 if (check
[i
].ccnoinherit
)
2808 /* Adjust Vars to match new table's column numbering */
2809 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2812 InvalidOid
, &found_whole_row
);
2815 * For the moment we have to reject whole-row variables. We
2816 * could convert them, if we knew the new table's rowtype OID,
2817 * but that hasn't been assigned yet.
2819 if (found_whole_row
)
2821 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2822 errmsg("cannot convert whole-row table reference"),
2823 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2825 RelationGetRelationName(relation
))));
2827 /* check for duplicate */
2828 if (!MergeCheckConstraint(constraints
, name
, expr
))
2830 /* nope, this is a new one */
2831 CookedConstraint
*cooked
;
2833 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
2834 cooked
->contype
= CONSTR_CHECK
;
2835 cooked
->conoid
= InvalidOid
; /* until created */
2836 cooked
->name
= pstrdup(name
);
2837 cooked
->attnum
= 0; /* not used for constraints */
2838 cooked
->expr
= expr
;
2839 cooked
->skip_validation
= false;
2840 cooked
->is_local
= false;
2841 cooked
->inhcount
= 1;
2842 cooked
->is_no_inherit
= false;
2843 constraints
= lappend(constraints
, cooked
);
2848 free_attrmap(newattmap
);
2851 * Close the parent rel, but keep our lock on it until xact commit.
2852 * That will prevent someone else from deleting or ALTERing the parent
2853 * before the child is committed.
2855 table_close(relation
, NoLock
);
2859 * If we had no inherited attributes, the result schema is just the
2860 * explicitly declared columns. Otherwise, we need to merge the declared
2861 * columns into the inherited schema list. Although, we never have any
2862 * explicitly declared columns if the table is a partition.
2864 if (inhSchema
!= NIL
)
2866 int schema_attno
= 0;
2868 foreach(entry
, schema
)
2870 ColumnDef
*newdef
= lfirst(entry
);
2871 char *attributeName
= newdef
->colname
;
2877 * Does it conflict with some previously inherited column?
2879 exist_attno
= findAttrByName(attributeName
, inhSchema
);
2880 if (exist_attno
> 0)
2891 * Partitions have only one parent and have no column
2892 * definitions of their own, so conflict should never occur.
2894 Assert(!is_partition
);
2897 * Yes, try to merge the two column definitions.
2899 if (exist_attno
== schema_attno
)
2901 (errmsg("merging column \"%s\" with inherited definition",
2905 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
2906 errdetail("User-specified column moved to the position of the inherited column.")));
2907 def
= (ColumnDef
*) list_nth(inhSchema
, exist_attno
- 1);
2910 * Must have the same type and typmod
2912 typenameTypeIdAndMod(NULL
, def
->typeName
, &defTypeId
, &deftypmod
);
2913 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newTypeId
, &newtypmod
);
2914 if (defTypeId
!= newTypeId
|| deftypmod
!= newtypmod
)
2916 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2917 errmsg("column \"%s\" has a type conflict",
2919 errdetail("%s versus %s",
2920 format_type_with_typemod(defTypeId
,
2922 format_type_with_typemod(newTypeId
,
2926 * Must have the same collation
2928 defcollid
= GetColumnDefCollation(NULL
, def
, defTypeId
);
2929 newcollid
= GetColumnDefCollation(NULL
, newdef
, newTypeId
);
2930 if (defcollid
!= newcollid
)
2932 (errcode(ERRCODE_COLLATION_MISMATCH
),
2933 errmsg("column \"%s\" has a collation conflict",
2935 errdetail("\"%s\" versus \"%s\"",
2936 get_collation_name(defcollid
),
2937 get_collation_name(newcollid
))));
2940 * Identity is never inherited. The new column can have an
2941 * identity definition, so we always just take that one.
2943 def
->identity
= newdef
->identity
;
2946 * Copy storage parameter
2948 if (def
->storage
== 0)
2949 def
->storage
= newdef
->storage
;
2950 else if (newdef
->storage
!= 0 && def
->storage
!= newdef
->storage
)
2952 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2953 errmsg("column \"%s\" has a storage parameter conflict",
2955 errdetail("%s versus %s",
2956 storage_name(def
->storage
),
2957 storage_name(newdef
->storage
))));
2960 * Copy compression parameter
2962 if (def
->compression
== NULL
)
2963 def
->compression
= newdef
->compression
;
2964 else if (newdef
->compression
!= NULL
)
2966 if (strcmp(def
->compression
, newdef
->compression
) != 0)
2968 (errcode(ERRCODE_DATATYPE_MISMATCH
),
2969 errmsg("column \"%s\" has a compression method conflict",
2971 errdetail("%s versus %s", def
->compression
, newdef
->compression
)));
2975 * Merge of NOT NULL constraints = OR 'em together
2977 def
->is_not_null
|= newdef
->is_not_null
;
2980 * Check for conflicts related to generated columns.
2982 * If the parent column is generated, the child column will be
2983 * made a generated column if it isn't already. If it is a
2984 * generated column, we'll take its generation expression in
2985 * preference to the parent's. We must check that the child
2986 * column doesn't specify a default value or identity, which
2987 * matches the rules for a single column in parse_utilcmd.c.
2989 * Conversely, if the parent column is not generated, the
2990 * child column can't be either. (We used to allow that, but
2991 * it results in being able to override the generation
2992 * expression via UPDATEs through the parent.)
2996 if (newdef
->raw_default
&& !newdef
->generated
)
2998 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2999 errmsg("column \"%s\" inherits from generated column but specifies default",
3001 if (newdef
->identity
)
3003 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3004 errmsg("column \"%s\" inherits from generated column but specifies identity",
3009 if (newdef
->generated
)
3011 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3012 errmsg("child column \"%s\" specifies generation expression",
3014 errhint("A child table column cannot be generated unless its parent column is.")));
3018 * If new def has a default, override previous default
3020 if (newdef
->raw_default
!= NULL
)
3022 def
->raw_default
= newdef
->raw_default
;
3023 def
->cooked_default
= newdef
->cooked_default
;
3026 /* Mark the column as locally defined */
3027 def
->is_local
= true;
3032 * No, attach new column to result schema
3034 inhSchema
= lappend(inhSchema
, newdef
);
3041 * Check that we haven't exceeded the legal # of columns after merging
3042 * in inherited columns.
3044 if (list_length(schema
) > MaxHeapAttributeNumber
)
3046 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
3047 errmsg("tables can have at most %d columns",
3048 MaxHeapAttributeNumber
)));
3052 * Now that we have the column definition list for a partition, we can
3053 * check whether the columns referenced in the column constraint specs
3054 * actually exist. Also, we merge parent's NOT NULL constraints and
3055 * defaults into each corresponding column definition.
3059 foreach(entry
, saved_schema
)
3061 ColumnDef
*restdef
= lfirst(entry
);
3067 ColumnDef
*coldef
= lfirst(l
);
3069 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
3072 coldef
->is_not_null
|= restdef
->is_not_null
;
3075 * Check for conflicts related to generated columns.
3077 * Same rules as above: generated-ness has to match the
3078 * parent, but the contents of the generation expression
3081 if (coldef
->generated
)
3083 if (restdef
->raw_default
&& !restdef
->generated
)
3085 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3086 errmsg("column \"%s\" inherits from generated column but specifies default",
3087 restdef
->colname
)));
3088 if (restdef
->identity
)
3090 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3091 errmsg("column \"%s\" inherits from generated column but specifies identity",
3092 restdef
->colname
)));
3096 if (restdef
->generated
)
3098 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3099 errmsg("child column \"%s\" specifies generation expression",
3101 errhint("A child table column cannot be generated unless its parent column is.")));
3105 * Override the parent's default value for this column
3106 * (coldef->cooked_default) with the partition's local
3107 * definition (restdef->raw_default), if there's one. It
3108 * should be physically impossible to get a cooked default
3109 * in the local definition or a raw default in the
3110 * inherited definition, but make sure they're nulls, for
3113 Assert(restdef
->cooked_default
== NULL
);
3114 Assert(coldef
->raw_default
== NULL
);
3115 if (restdef
->raw_default
)
3117 coldef
->raw_default
= restdef
->raw_default
;
3118 coldef
->cooked_default
= NULL
;
3123 /* complain for constraints on columns not in parent */
3126 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3127 errmsg("column \"%s\" does not exist",
3128 restdef
->colname
)));
3133 * If we found any conflicting parent default values, check to make sure
3134 * they were overridden by the child.
3136 if (have_bogus_defaults
)
3138 foreach(entry
, schema
)
3140 ColumnDef
*def
= lfirst(entry
);
3142 if (def
->cooked_default
== &bogus_marker
)
3146 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3147 errmsg("column \"%s\" inherits conflicting generation expressions",
3149 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3152 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3153 errmsg("column \"%s\" inherits conflicting default values",
3155 errhint("To resolve the conflict, specify a default explicitly.")));
3160 *supconstr
= constraints
;
3166 * MergeCheckConstraint
3167 * Try to merge an inherited CHECK constraint with previous ones
3169 * If we inherit identically-named constraints from multiple parents, we must
3170 * merge them, or throw an error if they don't have identical definitions.
3172 * constraints is a list of CookedConstraint structs for previous constraints.
3174 * Returns true if merged (constraint is a duplicate), or false if it's
3175 * got a so-far-unique name, or throws error if conflict.
3178 MergeCheckConstraint(List
*constraints
, char *name
, Node
*expr
)
3182 foreach(lc
, constraints
)
3184 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3186 Assert(ccon
->contype
== CONSTR_CHECK
);
3188 /* Non-matching names never conflict */
3189 if (strcmp(ccon
->name
, name
) != 0)
3192 if (equal(expr
, ccon
->expr
))
3196 if (ccon
->inhcount
< 0)
3198 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3199 errmsg("too many inheritance parents"));
3204 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3205 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3214 * StoreCatalogInheritance
3215 * Updates the system catalogs with proper inheritance information.
3217 * supers is a list of the OIDs of the new relation's direct ancestors.
3220 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3221 bool child_is_partition
)
3230 Assert(OidIsValid(relationId
));
3236 * Store INHERITS information in pg_inherits using direct ancestors only.
3237 * Also enter dependencies on the direct ancestors, and make sure they are
3238 * marked with relhassubclass = true.
3240 * (Once upon a time, both direct and indirect ancestors were found here
3241 * and then entered into pg_ipl. Since that catalog doesn't exist
3242 * anymore, there's no need to look for indirect ancestors.)
3244 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3247 foreach(entry
, supers
)
3249 Oid parentOid
= lfirst_oid(entry
);
3251 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3252 child_is_partition
);
3256 table_close(relation
, RowExclusiveLock
);
3260 * Make catalog entries showing relationId as being an inheritance child
3261 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3264 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3265 int32 seqNumber
, Relation inhRelation
,
3266 bool child_is_partition
)
3268 ObjectAddress childobject
,
3271 /* store the pg_inherits row */
3272 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3275 * Store a dependency too
3277 parentobject
.classId
= RelationRelationId
;
3278 parentobject
.objectId
= parentOid
;
3279 parentobject
.objectSubId
= 0;
3280 childobject
.classId
= RelationRelationId
;
3281 childobject
.objectId
= relationId
;
3282 childobject
.objectSubId
= 0;
3284 recordDependencyOn(&childobject
, &parentobject
,
3285 child_dependency_type(child_is_partition
));
3288 * Post creation hook of this inheritance. Since object_access_hook
3289 * doesn't take multiple object identifiers, we relay oid of parent
3290 * relation using auxiliary_id argument.
3292 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3297 * Mark the parent as having subclasses.
3299 SetRelationHasSubclass(parentOid
, true);
3303 * Look for an existing schema entry with the given name.
3305 * Returns the index (starting with 1) if attribute already exists in schema,
3309 findAttrByName(const char *attributeName
, List
*schema
)
3316 ColumnDef
*def
= lfirst(s
);
3318 if (strcmp(attributeName
, def
->colname
) == 0)
3328 * SetRelationHasSubclass
3329 * Set the value of the relation's relhassubclass field in pg_class.
3331 * NOTE: caller must be holding an appropriate lock on the relation.
3332 * ShareUpdateExclusiveLock is sufficient.
3334 * NOTE: an important side-effect of this operation is that an SI invalidation
3335 * message is sent out to all backends --- including me --- causing plans
3336 * referencing the relation to be rebuilt with the new list of children.
3337 * This must happen even if we find that no change is needed in the pg_class
3341 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3343 Relation relationRelation
;
3345 Form_pg_class classtuple
;
3348 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3350 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3351 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3352 if (!HeapTupleIsValid(tuple
))
3353 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3354 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3356 if (classtuple
->relhassubclass
!= relhassubclass
)
3358 classtuple
->relhassubclass
= relhassubclass
;
3359 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3363 /* no need to change tuple, but force relcache rebuild anyway */
3364 CacheInvalidateRelcacheByTuple(tuple
);
3367 heap_freetuple(tuple
);
3368 table_close(relationRelation
, RowExclusiveLock
);
3372 * CheckRelationTableSpaceMove
3373 * Check if relation can be moved to new tablespace.
3375 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3377 * Returns true if the relation can be moved to the new tablespace; raises
3378 * an error if it is not possible to do the move; returns false if the move
3379 * would have no effect.
3382 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3384 Oid oldTableSpaceId
;
3387 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3390 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3391 if (newTableSpaceId
== oldTableSpaceId
||
3392 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3396 * We cannot support moving mapped relations into different tablespaces.
3397 * (In particular this eliminates all shared catalogs.)
3399 if (RelationIsMapped(rel
))
3401 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3402 errmsg("cannot move system relation \"%s\"",
3403 RelationGetRelationName(rel
))));
3405 /* Cannot move a non-shared relation into pg_global */
3406 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3408 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3409 errmsg("only shared relations can be placed in pg_global tablespace")));
3412 * Do not allow moving temp tables of other backends ... their local
3413 * buffer manager is not going to cope.
3415 if (RELATION_IS_OTHER_TEMP(rel
))
3417 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3418 errmsg("cannot move temporary tables of other sessions")));
3424 * SetRelationTableSpace
3425 * Set new reltablespace and relfilenumber in pg_class entry.
3427 * newTableSpaceId is the new tablespace for the relation, and
3428 * newRelFilenumber its new filenumber. If newRelFilenumber is
3429 * InvalidRelFileNumber, this field is not updated.
3431 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3433 * The caller of this routine had better check if a relation can be
3434 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3435 * first, and is responsible for making the change visible with
3436 * CommandCounterIncrement().
3439 SetRelationTableSpace(Relation rel
,
3440 Oid newTableSpaceId
,
3441 RelFileNumber newRelFilenumber
)
3445 Form_pg_class rd_rel
;
3446 Oid reloid
= RelationGetRelid(rel
);
3448 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3450 /* Get a modifiable copy of the relation's pg_class row. */
3451 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3453 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3454 if (!HeapTupleIsValid(tuple
))
3455 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3456 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3458 /* Update the pg_class row. */
3459 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3460 InvalidOid
: newTableSpaceId
;
3461 if (RelFileNumberIsValid(newRelFilenumber
))
3462 rd_rel
->relfilenode
= newRelFilenumber
;
3463 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
3466 * Record dependency on tablespace. This is only required for relations
3467 * that have no physical storage.
3469 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3470 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3471 rd_rel
->reltablespace
);
3473 heap_freetuple(tuple
);
3474 table_close(pg_class
, RowExclusiveLock
);
3478 * renameatt_check - basic sanity checks before attribute rename
3481 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3483 char relkind
= classform
->relkind
;
3485 if (classform
->reloftype
&& !recursing
)
3487 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3488 errmsg("cannot rename column of typed table")));
3491 * Renaming the columns of sequences or toast tables doesn't actually
3492 * break anything from the system's point of view, since internal
3493 * references are by attnum. But it doesn't seem right to allow users to
3494 * change names that are hardcoded into the system, hence the following
3497 if (relkind
!= RELKIND_RELATION
&&
3498 relkind
!= RELKIND_VIEW
&&
3499 relkind
!= RELKIND_MATVIEW
&&
3500 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3501 relkind
!= RELKIND_INDEX
&&
3502 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3503 relkind
!= RELKIND_FOREIGN_TABLE
&&
3504 relkind
!= RELKIND_PARTITIONED_TABLE
)
3506 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3507 errmsg("cannot rename columns of relation \"%s\"",
3508 NameStr(classform
->relname
)),
3509 errdetail_relkind_not_supported(relkind
)));
3512 * permissions checking. only the owner of a class can change its schema.
3514 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3515 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3516 NameStr(classform
->relname
));
3517 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3519 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3520 errmsg("permission denied: \"%s\" is a system catalog",
3521 NameStr(classform
->relname
))));
3525 * renameatt_internal - workhorse for renameatt
3527 * Return value is the attribute number in the 'myrelid' relation.
3530 renameatt_internal(Oid myrelid
,
3531 const char *oldattname
,
3532 const char *newattname
,
3535 int expected_parents
,
3536 DropBehavior behavior
)
3538 Relation targetrelation
;
3539 Relation attrelation
;
3541 Form_pg_attribute attform
;
3545 * Grab an exclusive lock on the target table, which we will NOT release
3546 * until end of transaction.
3548 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3549 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3552 * if the 'recurse' flag is set then we are supposed to rename this
3553 * attribute in all classes that inherit from 'relname' (as well as in
3556 * any permissions or problems with duplicate attributes will cause the
3557 * whole transaction to abort, which is what we want -- all or nothing.
3567 * we need the number of parents for each child so that the recursive
3568 * calls to renameatt() can determine whether there are any parents
3569 * outside the inheritance hierarchy being processed.
3571 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3575 * find_all_inheritors does the recursive search of the inheritance
3576 * hierarchy, so all we have to do is process all of the relids in the
3577 * list that it returns.
3579 forboth(lo
, child_oids
, li
, child_numparents
)
3581 Oid childrelid
= lfirst_oid(lo
);
3582 int numparents
= lfirst_int(li
);
3584 if (childrelid
== myrelid
)
3586 /* note we need not recurse again */
3587 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3593 * If we are told not to recurse, there had better not be any child
3594 * tables; else the rename would put them out of step.
3596 * expected_parents will only be 0 if we are not already recursing.
3598 if (expected_parents
== 0 &&
3599 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3601 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3602 errmsg("inherited column \"%s\" must be renamed in child tables too",
3606 /* rename attributes in typed tables of composite type */
3607 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3612 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3613 RelationGetRelationName(targetrelation
),
3616 foreach(lo
, child_oids
)
3617 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3620 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3622 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3623 if (!HeapTupleIsValid(atttup
))
3625 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3626 errmsg("column \"%s\" does not exist",
3628 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3630 attnum
= attform
->attnum
;
3633 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3634 errmsg("cannot rename system column \"%s\"",
3638 * if the attribute is inherited, forbid the renaming. if this is a
3639 * top-level call to renameatt(), then expected_parents will be 0, so the
3640 * effect of this code will be to prohibit the renaming if the attribute
3641 * is inherited at all. if this is a recursive call to renameatt(),
3642 * expected_parents will be the number of parents the current relation has
3643 * within the inheritance hierarchy being processed, so we'll prohibit the
3644 * renaming only if there are additional parents from elsewhere.
3646 if (attform
->attinhcount
> expected_parents
)
3648 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3649 errmsg("cannot rename inherited column \"%s\"",
3652 /* new name should not already exist */
3653 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3655 /* apply the update */
3656 namestrcpy(&(attform
->attname
), newattname
);
3658 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3660 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3662 heap_freetuple(atttup
);
3664 table_close(attrelation
, RowExclusiveLock
);
3666 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3672 * Perform permissions and integrity checks before acquiring a relation lock.
3675 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3681 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3682 if (!HeapTupleIsValid(tuple
))
3683 return; /* concurrently dropped */
3684 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3685 renameatt_check(relid
, form
, false);
3686 ReleaseSysCache(tuple
);
3690 * renameatt - changes the name of an attribute in a relation
3692 * The returned ObjectAddress is that of the renamed column.
3695 renameatt(RenameStmt
*stmt
)
3699 ObjectAddress address
;
3701 /* lock level taken here should match renameatt_internal */
3702 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3703 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3704 RangeVarCallbackForRenameAttribute
,
3707 if (!OidIsValid(relid
))
3710 (errmsg("relation \"%s\" does not exist, skipping",
3711 stmt
->relation
->relname
)));
3712 return InvalidObjectAddress
;
3716 renameatt_internal(relid
,
3717 stmt
->subname
, /* old att name */
3718 stmt
->newname
, /* new att name */
3719 stmt
->relation
->inh
, /* recursive? */
3720 false, /* recursing? */
3721 0, /* expected inhcount */
3724 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3730 * same logic as renameatt_internal
3732 static ObjectAddress
3733 rename_constraint_internal(Oid myrelid
,
3735 const char *oldconname
,
3736 const char *newconname
,
3739 int expected_parents
)
3741 Relation targetrelation
= NULL
;
3744 Form_pg_constraint con
;
3745 ObjectAddress address
;
3747 Assert(!myrelid
|| !mytypid
);
3751 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
3755 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3758 * don't tell it whether we're recursing; we allow changing typed
3761 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
3763 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
3766 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
3767 if (!HeapTupleIsValid(tuple
))
3768 elog(ERROR
, "cache lookup failed for constraint %u",
3770 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
3772 if (myrelid
&& con
->contype
== CONSTRAINT_CHECK
&& !con
->connoinherit
)
3781 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3784 forboth(lo
, child_oids
, li
, child_numparents
)
3786 Oid childrelid
= lfirst_oid(lo
);
3787 int numparents
= lfirst_int(li
);
3789 if (childrelid
== myrelid
)
3792 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
3797 if (expected_parents
== 0 &&
3798 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3800 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3801 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3805 if (con
->coninhcount
> expected_parents
)
3807 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3808 errmsg("cannot rename inherited constraint \"%s\"",
3813 && (con
->contype
== CONSTRAINT_PRIMARY
3814 || con
->contype
== CONSTRAINT_UNIQUE
3815 || con
->contype
== CONSTRAINT_EXCLUSION
))
3816 /* rename the index; this renames the constraint as well */
3817 RenameRelationInternal(con
->conindid
, newconname
, false, true);
3819 RenameConstraintById(constraintOid
, newconname
);
3821 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
3823 ReleaseSysCache(tuple
);
3828 * Invalidate relcache so as others can see the new constraint name.
3830 CacheInvalidateRelcache(targetrelation
);
3832 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3839 RenameConstraint(RenameStmt
*stmt
)
3841 Oid relid
= InvalidOid
;
3842 Oid typid
= InvalidOid
;
3844 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
3849 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
3850 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
3851 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
3852 if (!HeapTupleIsValid(tup
))
3853 elog(ERROR
, "cache lookup failed for type %u", typid
);
3854 checkDomainOwner(tup
);
3855 ReleaseSysCache(tup
);
3856 table_close(rel
, NoLock
);
3860 /* lock level taken here should match rename_constraint_internal */
3861 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3862 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3863 RangeVarCallbackForRenameAttribute
,
3865 if (!OidIsValid(relid
))
3868 (errmsg("relation \"%s\" does not exist, skipping",
3869 stmt
->relation
->relname
)));
3870 return InvalidObjectAddress
;
3875 rename_constraint_internal(relid
, typid
,
3879 stmt
->relation
->inh
), /* recursive? */
3880 false, /* recursing? */
3881 0 /* expected inhcount */ );
3885 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
3889 RenameRelation(RenameStmt
*stmt
)
3891 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
3893 ObjectAddress address
;
3896 * Grab an exclusive lock on the target table, index, sequence, view,
3897 * materialized view, or foreign table, which we will NOT release until
3898 * end of transaction.
3900 * Lock level used here should match RenameRelationInternal, to avoid lock
3901 * escalation. However, because ALTER INDEX can be used with any relation
3902 * type, we mustn't believe without verification.
3910 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
3912 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
3913 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3914 RangeVarCallbackForAlterRelation
,
3917 if (!OidIsValid(relid
))
3920 (errmsg("relation \"%s\" does not exist, skipping",
3921 stmt
->relation
->relname
)));
3922 return InvalidObjectAddress
;
3926 * We allow mismatched statement and object types (e.g., ALTER INDEX
3927 * to rename a table), but we might've used the wrong lock level. If
3928 * that happens, retry with the correct lock level. We don't bother
3929 * if we already acquired AccessExclusiveLock with an index, however.
3931 relkind
= get_rel_relkind(relid
);
3932 obj_is_index
= (relkind
== RELKIND_INDEX
||
3933 relkind
== RELKIND_PARTITIONED_INDEX
);
3934 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
3937 UnlockRelationOid(relid
, lockmode
);
3938 is_index_stmt
= obj_is_index
;
3942 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
3944 ObjectAddressSet(address
, RelationRelationId
, relid
);
3950 * RenameRelationInternal - change the name of a relation
3953 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
3955 Relation targetrelation
;
3956 Relation relrelation
; /* for RELATION relation */
3958 Form_pg_class relform
;
3962 * Grab a lock on the target relation, which we will NOT release until end
3963 * of transaction. We need at least a self-exclusive lock so that
3964 * concurrent DDL doesn't overwrite the rename if they start updating
3965 * while still seeing the old version. The lock also guards against
3966 * triggering relcache reloads in concurrent sessions, which might not
3967 * handle this information changing under them. For indexes, we can use a
3968 * reduced lock level because RelationReloadIndexInfo() handles indexes
3971 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
3972 namespaceId
= RelationGetNamespace(targetrelation
);
3975 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
3977 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3979 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
3980 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
3981 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
3982 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
3984 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
3986 (errcode(ERRCODE_DUPLICATE_TABLE
),
3987 errmsg("relation \"%s\" already exists",
3991 * RenameRelation is careful not to believe the caller's idea of the
3992 * relation kind being handled. We don't have to worry about this, but
3993 * let's not be totally oblivious to it. We can process an index as
3994 * not-an-index, but not the other way around.
3997 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
3998 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4001 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4002 * because it's a copy...)
4004 namestrcpy(&(relform
->relname
), newrelname
);
4006 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4008 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4009 InvalidOid
, is_internal
);
4011 heap_freetuple(reltup
);
4012 table_close(relrelation
, RowExclusiveLock
);
4015 * Also rename the associated type, if any.
4017 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4018 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4019 newrelname
, namespaceId
);
4022 * Also rename the associated constraint, if any.
4024 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4025 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4027 Oid constraintId
= get_index_constraint(myrelid
);
4029 if (OidIsValid(constraintId
))
4030 RenameConstraintById(constraintId
, newrelname
);
4034 * Close rel, but keep lock!
4036 relation_close(targetrelation
, NoLock
);
4040 * ResetRelRewrite - reset relrewrite
4043 ResetRelRewrite(Oid myrelid
)
4045 Relation relrelation
; /* for RELATION relation */
4047 Form_pg_class relform
;
4050 * Find relation's pg_class tuple.
4052 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4054 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4055 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4056 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4057 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4060 * Update pg_class tuple.
4062 relform
->relrewrite
= InvalidOid
;
4064 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4066 heap_freetuple(reltup
);
4067 table_close(relrelation
, RowExclusiveLock
);
4071 * Disallow ALTER TABLE (and similar commands) when the current backend has
4072 * any open reference to the target table besides the one just acquired by
4073 * the calling command; this implies there's an open cursor or active plan.
4074 * We need this check because our lock doesn't protect us against stomping
4075 * on our own foot, only other people's feet!
4077 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4078 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4079 * possibly be relaxed to only error out for certain types of alterations.
4080 * But the use-case for allowing any of these things is not obvious, so we
4081 * won't work hard at it for now.
4083 * We also reject these commands if there are any pending AFTER trigger events
4084 * for the rel. This is certainly necessary for the rewriting variants of
4085 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4086 * events would try to fetch the wrong tuples. It might be overly cautious
4087 * in other cases, but again it seems better to err on the side of paranoia.
4089 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4090 * we are worried about active indexscans on the index. The trigger-event
4091 * check can be skipped, since we are doing no damage to the parent table.
4093 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4096 CheckTableNotInUse(Relation rel
, const char *stmt
)
4098 int expected_refcnt
;
4100 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4101 if (rel
->rd_refcnt
!= expected_refcnt
)
4103 (errcode(ERRCODE_OBJECT_IN_USE
),
4104 /* translator: first %s is a SQL command, eg ALTER TABLE */
4105 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4106 stmt
, RelationGetRelationName(rel
))));
4108 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4109 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4110 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4112 (errcode(ERRCODE_OBJECT_IN_USE
),
4113 /* translator: first %s is a SQL command, eg ALTER TABLE */
4114 errmsg("cannot %s \"%s\" because it has pending trigger events",
4115 stmt
, RelationGetRelationName(rel
))));
4119 * AlterTableLookupRelation
4120 * Look up, and lock, the OID for the relation named by an alter table
4124 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4126 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4127 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4128 RangeVarCallbackForAlterRelation
,
4134 * Execute ALTER TABLE, which can be a list of subcommands
4136 * ALTER TABLE is performed in three phases:
4137 * 1. Examine subcommands and perform pre-transformation checking.
4138 * 2. Validate and transform subcommands, and update system catalogs.
4139 * 3. Scan table(s) to check new constraints, and optionally recopy
4140 * the data into new table(s).
4141 * Phase 3 is not performed unless one or more of the subcommands requires
4142 * it. The intention of this design is to allow multiple independent
4143 * updates of the table schema to be performed with only one pass over the
4146 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4147 * each table to be affected (there may be multiple affected tables if the
4148 * commands traverse a table inheritance hierarchy). Also we do preliminary
4149 * validation of the subcommands. Because earlier subcommands may change
4150 * the catalog state seen by later commands, there are limits to what can
4151 * be done in this phase. Generally, this phase acquires table locks,
4152 * checks permissions and relkind, and recurses to find child tables.
4154 * ATRewriteCatalogs performs phase 2 for each affected table.
4155 * Certain subcommands need to be performed before others to avoid
4156 * unnecessary conflicts; for example, DROP COLUMN should come before
4157 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4158 * lists, one for each logical "pass" of phase 2.
4160 * ATRewriteTables performs phase 3 for those tables that need it.
4162 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4163 * since phase 1 already does it. However, for certain subcommand types
4164 * it is only possible to determine how to recurse at phase 2 time; for
4165 * those cases, phase 1 sets the cmd->recurse flag.
4167 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4168 * the whole operation; we don't have to do anything special to clean up.
4170 * The caller must lock the relation, with an appropriate lock level
4171 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4172 * or higher. We pass the lock level down
4173 * so that we can apply it recursively to inherited tables. Note that the
4174 * lock level we want as we recurse might well be higher than required for
4175 * that specific subcommand. So we pass down the overall lock requirement,
4176 * rather than reassess it at lower levels.
4178 * The caller also provides a "context" which is to be passed back to
4179 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4180 * Some of the fields therein, such as the relid, are used here as well.
4183 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4184 AlterTableUtilityContext
*context
)
4188 /* Caller is required to provide an adequate lock. */
4189 rel
= relation_open(context
->relid
, NoLock
);
4191 CheckTableNotInUse(rel
, "ALTER TABLE");
4193 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4197 * AlterTableInternal
4199 * ALTER TABLE with target specified by OID
4201 * We do not reject if the relation is already open, because it's quite
4202 * likely that one or more layers of caller have it open. That means it
4203 * is unsafe to use this entry point for alterations that could break
4204 * existing query plans. On the assumption it's not used for such, we
4205 * don't have to reject pending AFTER triggers, either.
4207 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4208 * used for any subcommand types that require parse transformation or
4209 * could generate subcommands that have to be passed to ProcessUtility.
4212 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4215 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4217 rel
= relation_open(relid
, lockmode
);
4219 EventTriggerAlterTableRelid(relid
);
4221 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4225 * AlterTableGetLockLevel
4227 * Sets the overall lock level required for the supplied list of subcommands.
4228 * Policy for doing this set according to needs of AlterTable(), see
4229 * comments there for overall explanation.
4231 * Function is called before and after parsing, so it must give same
4232 * answer each time it is called. Some subcommands are transformed
4233 * into other subcommand types, so the transform must never be made to a
4234 * lower lock level than previously assigned. All transforms are noted below.
4236 * Since this is called before we lock the table we cannot use table metadata
4237 * to influence the type of lock we acquire.
4239 * There should be no lockmodes hardcoded into the subcommand functions. All
4240 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4241 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4242 * and does not travel through this section of code and cannot be combined with
4243 * any of the subcommands given here.
4245 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4246 * so any changes that might affect SELECTs running on standbys need to use
4247 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4248 * have a solution for that also.
4250 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4251 * that takes a lock less than AccessExclusiveLock can change object definitions
4252 * while pg_dump is running. Be careful to check that the appropriate data is
4253 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4254 * otherwise we might end up with an inconsistent dump that can't restore.
4257 AlterTableGetLockLevel(List
*cmds
)
4260 * This only works if we read catalog tables using MVCC snapshots.
4263 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4267 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4268 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4270 switch (cmd
->subtype
)
4273 * These subcommands rewrite the heap, so require full locks.
4275 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4277 case AT_SetAccessMethod
: /* must rewrite heap */
4278 case AT_SetTableSpace
: /* must rewrite heap */
4279 case AT_AlterColumnType
: /* must rewrite heap */
4280 cmd_lockmode
= AccessExclusiveLock
;
4284 * These subcommands may require addition of toast tables. If
4285 * we add a toast table to a table currently being scanned, we
4286 * might miss data added to the new toast table by concurrent
4287 * insert transactions.
4289 case AT_SetStorage
: /* may add toast tables, see
4290 * ATRewriteCatalogs() */
4291 cmd_lockmode
= AccessExclusiveLock
;
4295 * Removing constraints can affect SELECTs that have been
4296 * optimized assuming the constraint holds true. See also
4297 * CloneFkReferenced.
4299 case AT_DropConstraint
: /* as DROP INDEX */
4300 case AT_DropNotNull
: /* may change some SQL plans */
4301 cmd_lockmode
= AccessExclusiveLock
;
4305 * Subcommands that may be visible to concurrent SELECTs
4307 case AT_DropColumn
: /* change visible to SELECT */
4308 case AT_AddColumnToView
: /* CREATE VIEW */
4309 case AT_DropOids
: /* used to equiv to DropColumn */
4310 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4311 case AT_EnableReplicaRule
: /* may change SELECT rules */
4312 case AT_EnableRule
: /* may change SELECT rules */
4313 case AT_DisableRule
: /* may change SELECT rules */
4314 cmd_lockmode
= AccessExclusiveLock
;
4318 * Changing owner may remove implicit SELECT privileges
4320 case AT_ChangeOwner
: /* change visible to SELECT */
4321 cmd_lockmode
= AccessExclusiveLock
;
4325 * Changing foreign table options may affect optimization.
4327 case AT_GenericOptions
:
4328 case AT_AlterColumnGenericOptions
:
4329 cmd_lockmode
= AccessExclusiveLock
;
4333 * These subcommands affect write operations only.
4336 case AT_EnableAlwaysTrig
:
4337 case AT_EnableReplicaTrig
:
4338 case AT_EnableTrigAll
:
4339 case AT_EnableTrigUser
:
4340 case AT_DisableTrig
:
4341 case AT_DisableTrigAll
:
4342 case AT_DisableTrigUser
:
4343 cmd_lockmode
= ShareRowExclusiveLock
;
4347 * These subcommands affect write operations only. XXX
4348 * Theoretically, these could be ShareRowExclusiveLock.
4350 case AT_ColumnDefault
:
4351 case AT_CookedColumnDefault
:
4352 case AT_AlterConstraint
:
4353 case AT_AddIndex
: /* from ADD CONSTRAINT */
4354 case AT_AddIndexConstraint
:
4355 case AT_ReplicaIdentity
:
4357 case AT_EnableRowSecurity
:
4358 case AT_DisableRowSecurity
:
4359 case AT_ForceRowSecurity
:
4360 case AT_NoForceRowSecurity
:
4361 case AT_AddIdentity
:
4362 case AT_DropIdentity
:
4363 case AT_SetIdentity
:
4364 case AT_DropExpression
:
4365 case AT_SetCompression
:
4366 cmd_lockmode
= AccessExclusiveLock
;
4369 case AT_AddConstraint
:
4370 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4371 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4372 if (IsA(cmd
->def
, Constraint
))
4374 Constraint
*con
= (Constraint
*) cmd
->def
;
4376 switch (con
->contype
)
4378 case CONSTR_EXCLUSION
:
4379 case CONSTR_PRIMARY
:
4383 * Cases essentially the same as CREATE INDEX. We
4384 * could reduce the lock strength to ShareLock if
4385 * we can work out how to allow concurrent catalog
4386 * updates. XXX Might be set down to
4387 * ShareRowExclusiveLock but requires further
4390 cmd_lockmode
= AccessExclusiveLock
;
4392 case CONSTR_FOREIGN
:
4395 * We add triggers to both tables when we add a
4396 * Foreign Key, so the lock level must be at least
4397 * as strong as CREATE TRIGGER.
4399 cmd_lockmode
= ShareRowExclusiveLock
;
4403 cmd_lockmode
= AccessExclusiveLock
;
4409 * These subcommands affect inheritance behaviour. Queries
4410 * started before us will continue to see the old inheritance
4411 * behaviour, while queries started after we commit will see
4412 * new behaviour. No need to prevent reads or writes to the
4413 * subtable while we hook it up though. Changing the TupDesc
4414 * may be a problem, so keep highest lock.
4417 case AT_DropInherit
:
4418 cmd_lockmode
= AccessExclusiveLock
;
4422 * These subcommands affect implicit row type conversion. They
4423 * have affects similar to CREATE/DROP CAST on queries. don't
4424 * provide for invalidating parse trees as a result of such
4425 * changes, so we keep these at AccessExclusiveLock.
4429 cmd_lockmode
= AccessExclusiveLock
;
4433 * Only used by CREATE OR REPLACE VIEW which must conflict
4434 * with an SELECTs currently using the view.
4436 case AT_ReplaceRelOptions
:
4437 cmd_lockmode
= AccessExclusiveLock
;
4441 * These subcommands affect general strategies for performance
4442 * and maintenance, though don't change the semantic results
4443 * from normal data reads and writes. Delaying an ALTER TABLE
4444 * behind currently active writes only delays the point where
4445 * the new strategy begins to take effect, so there is no
4446 * benefit in waiting. In this case the minimum restriction
4447 * applies: we don't currently allow concurrent catalog
4450 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4451 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4452 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4453 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4454 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4455 cmd_lockmode
= ShareUpdateExclusiveLock
;
4459 case AT_SetUnLogged
:
4460 cmd_lockmode
= AccessExclusiveLock
;
4463 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4464 cmd_lockmode
= ShareUpdateExclusiveLock
;
4468 * Rel options are more complex than first appears. Options
4469 * are set here for tables, views and indexes; for historical
4470 * reasons these can all be used with ALTER TABLE, so we can't
4471 * decide between them using the basic grammar.
4473 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4475 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4477 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4480 case AT_AttachPartition
:
4481 cmd_lockmode
= ShareUpdateExclusiveLock
;
4484 case AT_DetachPartition
:
4485 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4486 cmd_lockmode
= ShareUpdateExclusiveLock
;
4488 cmd_lockmode
= AccessExclusiveLock
;
4491 case AT_DetachPartitionFinalize
:
4492 cmd_lockmode
= ShareUpdateExclusiveLock
;
4495 case AT_CheckNotNull
:
4498 * This only examines the table's schema; but lock must be
4499 * strong enough to prevent concurrent DROP NOT NULL.
4501 cmd_lockmode
= AccessShareLock
;
4505 elog(ERROR
, "unrecognized alter table type: %d",
4506 (int) cmd
->subtype
);
4511 * Take the greatest lockmode from any subcommand
4513 if (cmd_lockmode
> lockmode
)
4514 lockmode
= cmd_lockmode
;
4521 * ATController provides top level control over the phases.
4523 * parsetree is passed in to allow it to be passed to event triggers
4527 ATController(AlterTableStmt
*parsetree
,
4528 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4529 AlterTableUtilityContext
*context
)
4534 /* Phase 1: preliminary examination of commands, create work queue */
4537 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4539 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4542 /* Close the relation, but keep lock until commit */
4543 relation_close(rel
, NoLock
);
4545 /* Phase 2: update system catalogs */
4546 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4548 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4549 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4555 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4556 * recursion and permission checks.
4558 * Caller must have acquired appropriate lock type on relation already.
4559 * This lock should be held until commit.
4562 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4563 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4564 AlterTableUtilityContext
*context
)
4566 AlteredTableInfo
*tab
;
4567 int pass
= AT_PASS_UNSET
;
4569 /* Find or create work queue entry for this table */
4570 tab
= ATGetQueueEntry(wqueue
, rel
);
4573 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4574 * partitions that are pending detach.
4576 if (rel
->rd_rel
->relispartition
&&
4577 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4578 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4580 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4581 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4582 RelationGetRelationName(rel
)),
4583 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4586 * Copy the original subcommand for each table, so we can scribble on it.
4587 * This avoids conflicts when different child tables need to make
4588 * different parse transformations (for example, the same column may have
4589 * different column numbers in different children).
4591 cmd
= copyObject(cmd
);
4594 * Do permissions and relkind checking, recursion to child tables if
4595 * needed, and any additional phase-1 processing needed. (But beware of
4596 * adding any processing that looks at table details that another
4597 * subcommand could change. In some cases we reject multiple subcommands
4598 * that could try to change the same state in contrary ways.)
4600 switch (cmd
->subtype
)
4602 case AT_AddColumn
: /* ADD COLUMN */
4603 ATSimplePermissions(cmd
->subtype
, rel
,
4604 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4605 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4607 /* Recursion occurs during execution phase */
4608 pass
= AT_PASS_ADD_COL
;
4610 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4611 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4612 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4614 /* Recursion occurs during execution phase */
4615 pass
= AT_PASS_ADD_COL
;
4617 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4620 * We allow defaults on views so that INSERT into a view can have
4621 * default-ish behavior. This works because the rewriter
4622 * substitutes default values into INSERTs before it expands
4625 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4626 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4627 /* No command-specific prep needed */
4628 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4630 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4631 /* This is currently used only in CREATE TABLE */
4632 /* (so the permission check really isn't necessary) */
4633 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4634 /* This command never recurses */
4635 pass
= AT_PASS_ADD_OTHERCONSTR
;
4637 case AT_AddIdentity
:
4638 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4639 /* This command never recurses */
4640 pass
= AT_PASS_ADD_OTHERCONSTR
;
4642 case AT_SetIdentity
:
4643 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4644 /* This command never recurses */
4645 /* This should run after AddIdentity, so do it in MISC pass */
4646 pass
= AT_PASS_MISC
;
4648 case AT_DropIdentity
:
4649 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4650 /* This command never recurses */
4651 pass
= AT_PASS_DROP
;
4653 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4654 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4655 ATPrepDropNotNull(rel
, recurse
, recursing
);
4656 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4657 pass
= AT_PASS_DROP
;
4659 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4660 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4661 /* Need command-specific recursion decision */
4662 ATPrepSetNotNull(wqueue
, rel
, cmd
, recurse
, recursing
,
4664 pass
= AT_PASS_COL_ATTRS
;
4666 case AT_CheckNotNull
: /* check column is already marked NOT NULL */
4667 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4668 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4669 /* No command-specific prep needed */
4670 pass
= AT_PASS_COL_ATTRS
;
4672 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4673 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4674 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4675 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4676 pass
= AT_PASS_DROP
;
4678 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4679 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4680 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4681 /* No command-specific prep needed */
4682 pass
= AT_PASS_MISC
;
4684 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4685 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4686 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4687 /* This command never recurses */
4688 pass
= AT_PASS_MISC
;
4690 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4691 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4692 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4693 /* No command-specific prep needed */
4694 pass
= AT_PASS_MISC
;
4696 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4697 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4698 /* This command never recurses */
4699 /* No command-specific prep needed */
4700 pass
= AT_PASS_MISC
;
4702 case AT_DropColumn
: /* DROP COLUMN */
4703 ATSimplePermissions(cmd
->subtype
, rel
,
4704 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4705 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
4707 /* Recursion occurs during execution phase */
4708 pass
= AT_PASS_DROP
;
4710 case AT_AddIndex
: /* ADD INDEX */
4711 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4712 /* This command never recurses */
4713 /* No command-specific prep needed */
4714 pass
= AT_PASS_ADD_INDEX
;
4716 case AT_AddConstraint
: /* ADD CONSTRAINT */
4717 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4718 /* Recursion occurs during execution phase */
4719 /* No command-specific prep needed except saving recurse flag */
4721 cmd
->recurse
= true;
4722 pass
= AT_PASS_ADD_CONSTR
;
4724 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
4725 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4726 /* This command never recurses */
4727 /* No command-specific prep needed */
4728 pass
= AT_PASS_ADD_INDEXCONSTR
;
4730 case AT_DropConstraint
: /* DROP CONSTRAINT */
4731 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4732 ATCheckPartitionsNotInUse(rel
, lockmode
);
4733 /* Other recursion occurs during execution phase */
4734 /* No command-specific prep needed except saving recurse flag */
4736 cmd
->recurse
= true;
4737 pass
= AT_PASS_DROP
;
4739 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
4740 ATSimplePermissions(cmd
->subtype
, rel
,
4741 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4742 /* See comments for ATPrepAlterColumnType */
4743 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
4744 AT_PASS_UNSET
, context
);
4745 Assert(cmd
!= NULL
);
4746 /* Performs own recursion */
4747 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
4749 pass
= AT_PASS_ALTER_TYPE
;
4751 case AT_AlterColumnGenericOptions
:
4752 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
4753 /* This command never recurses */
4754 /* No command-specific prep needed */
4755 pass
= AT_PASS_MISC
;
4757 case AT_ChangeOwner
: /* ALTER OWNER */
4758 /* This command never recurses */
4759 /* No command-specific prep needed */
4760 pass
= AT_PASS_MISC
;
4762 case AT_ClusterOn
: /* CLUSTER ON */
4763 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
4764 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4765 /* These commands never recurse */
4766 /* No command-specific prep needed */
4767 pass
= AT_PASS_MISC
;
4769 case AT_SetLogged
: /* SET LOGGED */
4770 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4771 if (tab
->chgPersistence
)
4773 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4774 errmsg("cannot change persistence setting twice")));
4775 tab
->chgPersistence
= ATPrepChangePersistence(rel
, true);
4776 /* force rewrite if necessary; see comment in ATRewriteTables */
4777 if (tab
->chgPersistence
)
4779 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4780 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
4782 pass
= AT_PASS_MISC
;
4784 case AT_SetUnLogged
: /* SET UNLOGGED */
4785 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4786 if (tab
->chgPersistence
)
4788 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4789 errmsg("cannot change persistence setting twice")));
4790 tab
->chgPersistence
= ATPrepChangePersistence(rel
, false);
4791 /* force rewrite if necessary; see comment in ATRewriteTables */
4792 if (tab
->chgPersistence
)
4794 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4795 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
4797 pass
= AT_PASS_MISC
;
4799 case AT_DropOids
: /* SET WITHOUT OIDS */
4800 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4801 pass
= AT_PASS_DROP
;
4803 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
4804 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4806 /* partitioned tables don't have an access method */
4807 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
4809 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
4810 errmsg("cannot change access method of a partitioned table")));
4812 /* check if another access method change was already requested */
4813 if (OidIsValid(tab
->newAccessMethod
))
4815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4816 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
4818 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
4819 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
4821 case AT_SetTableSpace
: /* SET TABLESPACE */
4822 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
|
4823 ATT_PARTITIONED_INDEX
);
4824 /* This command never recurses */
4825 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
4826 pass
= AT_PASS_MISC
; /* doesn't actually matter */
4828 case AT_SetRelOptions
: /* SET (...) */
4829 case AT_ResetRelOptions
: /* RESET (...) */
4830 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
4831 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_MATVIEW
| ATT_INDEX
);
4832 /* This command never recurses */
4833 /* No command-specific prep needed */
4834 pass
= AT_PASS_MISC
;
4836 case AT_AddInherit
: /* INHERIT */
4837 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4838 /* This command never recurses */
4839 ATPrepAddInherit(rel
);
4840 pass
= AT_PASS_MISC
;
4842 case AT_DropInherit
: /* NO INHERIT */
4843 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4844 /* This command never recurses */
4845 /* No command-specific prep needed */
4846 pass
= AT_PASS_MISC
;
4848 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
4849 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4850 /* Recursion occurs during execution phase */
4851 pass
= AT_PASS_MISC
;
4853 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
4854 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4855 /* Recursion occurs during execution phase */
4856 /* No command-specific prep needed except saving recurse flag */
4858 cmd
->recurse
= true;
4859 pass
= AT_PASS_MISC
;
4861 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
4862 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4863 pass
= AT_PASS_MISC
;
4864 /* This command never recurses */
4865 /* No command-specific prep needed */
4867 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
4868 case AT_EnableAlwaysTrig
:
4869 case AT_EnableReplicaTrig
:
4870 case AT_EnableTrigAll
:
4871 case AT_EnableTrigUser
:
4872 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
4873 case AT_DisableTrigAll
:
4874 case AT_DisableTrigUser
:
4875 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4876 /* Set up recursion for phase 2; no other prep needed */
4878 cmd
->recurse
= true;
4879 pass
= AT_PASS_MISC
;
4881 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
4882 case AT_EnableAlwaysRule
:
4883 case AT_EnableReplicaRule
:
4884 case AT_DisableRule
:
4885 case AT_AddOf
: /* OF */
4886 case AT_DropOf
: /* NOT OF */
4887 case AT_EnableRowSecurity
:
4888 case AT_DisableRowSecurity
:
4889 case AT_ForceRowSecurity
:
4890 case AT_NoForceRowSecurity
:
4891 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4892 /* These commands never recurse */
4893 /* No command-specific prep needed */
4894 pass
= AT_PASS_MISC
;
4896 case AT_GenericOptions
:
4897 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
4898 /* No command-specific prep needed */
4899 pass
= AT_PASS_MISC
;
4901 case AT_AttachPartition
:
4902 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_INDEX
);
4903 /* No command-specific prep needed */
4904 pass
= AT_PASS_MISC
;
4906 case AT_DetachPartition
:
4907 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4908 /* No command-specific prep needed */
4909 pass
= AT_PASS_MISC
;
4911 case AT_DetachPartitionFinalize
:
4912 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4913 /* No command-specific prep needed */
4914 pass
= AT_PASS_MISC
;
4917 elog(ERROR
, "unrecognized alter table type: %d",
4918 (int) cmd
->subtype
);
4919 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
4922 Assert(pass
> AT_PASS_UNSET
);
4924 /* Add the subcommand to the appropriate list for phase 2 */
4925 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
4931 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
4932 * dispatched in a "safe" execution order (designed to avoid unnecessary
4936 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
4937 AlterTableUtilityContext
*context
)
4943 * We process all the tables "in parallel", one pass at a time. This is
4944 * needed because we may have to propagate work from one table to another
4945 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
4946 * re-adding of the foreign key constraint to the other table). Work can
4947 * only be propagated into later passes, however.
4949 for (pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
4951 /* Go through each table that needs to be processed */
4952 foreach(ltab
, *wqueue
)
4954 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
4955 List
*subcmds
= tab
->subcmds
[pass
];
4962 * Open the relation and store it in tab. This allows subroutines
4963 * close and reopen, if necessary. Appropriate lock was obtained
4964 * by phase 1, needn't get it again.
4966 tab
->rel
= relation_open(tab
->relid
, NoLock
);
4968 foreach(lcmd
, subcmds
)
4969 ATExecCmd(wqueue
, tab
,
4970 lfirst_node(AlterTableCmd
, lcmd
),
4971 lockmode
, pass
, context
);
4974 * After the ALTER TYPE pass, do cleanup work (this is not done in
4975 * ATExecAlterColumnType since it should be done only once if
4976 * multiple columns of a table are altered).
4978 if (pass
== AT_PASS_ALTER_TYPE
)
4979 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
4983 relation_close(tab
->rel
, NoLock
);
4989 /* Check to see if a toast table must be added. */
4990 foreach(ltab
, *wqueue
)
4992 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
4995 * If the table is source table of ATTACH PARTITION command, we did
4996 * not modify anything about it that will change its toasting
4997 * requirement, so no need to check.
4999 if (((tab
->relkind
== RELKIND_RELATION
||
5000 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5001 tab
->partition_constraint
== NULL
) ||
5002 tab
->relkind
== RELKIND_MATVIEW
)
5003 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5008 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5011 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5012 AlterTableCmd
*cmd
, LOCKMODE lockmode
, int cur_pass
,
5013 AlterTableUtilityContext
*context
)
5015 ObjectAddress address
= InvalidObjectAddress
;
5016 Relation rel
= tab
->rel
;
5018 switch (cmd
->subtype
)
5020 case AT_AddColumn
: /* ADD COLUMN */
5021 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5022 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5023 cmd
->recurse
, false,
5024 lockmode
, cur_pass
, context
);
5026 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5027 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5029 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5030 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5032 case AT_AddIdentity
:
5033 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5035 Assert(cmd
!= NULL
);
5036 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
);
5038 case AT_SetIdentity
:
5039 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5041 Assert(cmd
!= NULL
);
5042 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
);
5044 case AT_DropIdentity
:
5045 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5047 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5048 address
= ATExecDropNotNull(rel
, cmd
->name
, lockmode
);
5050 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5051 address
= ATExecSetNotNull(tab
, rel
, cmd
->name
, lockmode
);
5053 case AT_CheckNotNull
: /* check column is already marked NOT NULL */
5054 ATExecCheckNotNull(tab
, rel
, cmd
->name
, lockmode
);
5056 case AT_DropExpression
:
5057 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5059 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5060 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5062 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5063 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5065 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5066 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5068 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5069 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5071 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5072 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5075 case AT_DropColumn
: /* DROP COLUMN */
5076 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5077 cmd
->behavior
, cmd
->recurse
, false,
5078 cmd
->missing_ok
, lockmode
,
5081 case AT_AddIndex
: /* ADD INDEX */
5082 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5085 case AT_ReAddIndex
: /* ADD INDEX */
5086 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5089 case AT_ReAddStatistics
: /* ADD STATISTICS */
5090 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5093 case AT_AddConstraint
: /* ADD CONSTRAINT */
5094 /* Transform the command only during initial examination */
5095 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5096 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5097 cmd
->recurse
, lockmode
,
5099 /* Depending on constraint type, might be no more work to do now */
5102 ATExecAddConstraint(wqueue
, tab
, rel
,
5103 (Constraint
*) cmd
->def
,
5104 cmd
->recurse
, false, lockmode
);
5106 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5108 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5109 true, true, lockmode
);
5111 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5114 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5115 ((AlterDomainStmt
*) cmd
->def
)->def
,
5118 case AT_ReAddComment
: /* Re-add existing comment */
5119 address
= CommentObject((CommentStmt
*) cmd
->def
);
5121 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5122 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5125 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5126 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5128 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5129 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5132 case AT_DropConstraint
: /* DROP CONSTRAINT */
5133 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5134 cmd
->recurse
, false,
5135 cmd
->missing_ok
, lockmode
);
5137 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5138 /* parse transformation was done earlier */
5139 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5141 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5143 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5144 (List
*) cmd
->def
, lockmode
);
5146 case AT_ChangeOwner
: /* ALTER OWNER */
5147 ATExecChangeOwner(RelationGetRelid(rel
),
5148 get_rolespec_oid(cmd
->newowner
, false),
5151 case AT_ClusterOn
: /* CLUSTER ON */
5152 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5154 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5155 ATExecDropCluster(rel
, lockmode
);
5157 case AT_SetLogged
: /* SET LOGGED */
5158 case AT_SetUnLogged
: /* SET UNLOGGED */
5160 case AT_DropOids
: /* SET WITHOUT OIDS */
5161 /* nothing to do here, oid columns don't exist anymore */
5163 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5164 /* handled specially in Phase 3 */
5166 case AT_SetTableSpace
: /* SET TABLESPACE */
5169 * Only do this for partitioned tables and indexes, for which this
5170 * is just a catalog change. Other relation types which have
5171 * storage are handled by Phase 3.
5173 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5174 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5175 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5178 case AT_SetRelOptions
: /* SET (...) */
5179 case AT_ResetRelOptions
: /* RESET (...) */
5180 case AT_ReplaceRelOptions
: /* replace entire option list */
5181 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5183 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5184 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5185 TRIGGER_FIRES_ON_ORIGIN
, false,
5189 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5190 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5191 TRIGGER_FIRES_ALWAYS
, false,
5195 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5196 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5197 TRIGGER_FIRES_ON_REPLICA
, false,
5201 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5202 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5203 TRIGGER_DISABLED
, false,
5207 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5208 ATExecEnableDisableTrigger(rel
, NULL
,
5209 TRIGGER_FIRES_ON_ORIGIN
, false,
5213 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5214 ATExecEnableDisableTrigger(rel
, NULL
,
5215 TRIGGER_DISABLED
, false,
5219 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5220 ATExecEnableDisableTrigger(rel
, NULL
,
5221 TRIGGER_FIRES_ON_ORIGIN
, true,
5225 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5226 ATExecEnableDisableTrigger(rel
, NULL
,
5227 TRIGGER_DISABLED
, true,
5232 case AT_EnableRule
: /* ENABLE RULE name */
5233 ATExecEnableDisableRule(rel
, cmd
->name
,
5234 RULE_FIRES_ON_ORIGIN
, lockmode
);
5236 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5237 ATExecEnableDisableRule(rel
, cmd
->name
,
5238 RULE_FIRES_ALWAYS
, lockmode
);
5240 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5241 ATExecEnableDisableRule(rel
, cmd
->name
,
5242 RULE_FIRES_ON_REPLICA
, lockmode
);
5244 case AT_DisableRule
: /* DISABLE RULE name */
5245 ATExecEnableDisableRule(rel
, cmd
->name
,
5246 RULE_DISABLED
, lockmode
);
5250 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5252 case AT_DropInherit
:
5253 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5256 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5259 ATExecDropOf(rel
, lockmode
);
5261 case AT_ReplicaIdentity
:
5262 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5264 case AT_EnableRowSecurity
:
5265 ATExecSetRowSecurity(rel
, true);
5267 case AT_DisableRowSecurity
:
5268 ATExecSetRowSecurity(rel
, false);
5270 case AT_ForceRowSecurity
:
5271 ATExecForceNoForceRowSecurity(rel
, true);
5273 case AT_NoForceRowSecurity
:
5274 ATExecForceNoForceRowSecurity(rel
, false);
5276 case AT_GenericOptions
:
5277 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5279 case AT_AttachPartition
:
5280 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5282 Assert(cmd
!= NULL
);
5283 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5284 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5287 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5288 ((PartitionCmd
*) cmd
->def
)->name
);
5290 case AT_DetachPartition
:
5291 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5293 Assert(cmd
!= NULL
);
5294 /* ATPrepCmd ensures it must be a table */
5295 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5296 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5297 ((PartitionCmd
*) cmd
->def
)->name
,
5298 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5300 case AT_DetachPartitionFinalize
:
5301 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5304 elog(ERROR
, "unrecognized alter table type: %d",
5305 (int) cmd
->subtype
);
5310 * Report the subcommand to interested event triggers.
5313 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5316 * Bump the command counter to ensure the next subcommand in the sequence
5317 * can see the changes so far
5319 CommandCounterIncrement();
5323 * ATParseTransformCmd: perform parse transformation for one subcommand
5325 * Returns the transformed subcommand tree, if there is one, else NULL.
5327 * The parser may hand back additional AlterTableCmd(s) and/or other
5328 * utility statements, either before or after the original subcommand.
5329 * Other AlterTableCmds are scheduled into the appropriate slot of the
5330 * AlteredTableInfo (they had better be for later passes than the current one).
5331 * Utility statements that are supposed to happen before the AlterTableCmd
5332 * are executed immediately. Those that are supposed to happen afterwards
5333 * are added to the tab->afterStmts list to be done at the very end.
5335 static AlterTableCmd
*
5336 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5337 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5338 int cur_pass
, AlterTableUtilityContext
*context
)
5340 AlterTableCmd
*newcmd
= NULL
;
5341 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5346 /* Gin up an AlterTableStmt with just this subcommand and this table */
5348 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5349 pstrdup(RelationGetRelationName(rel
)),
5351 atstmt
->relation
->inh
= recurse
;
5352 atstmt
->cmds
= list_make1(cmd
);
5353 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5354 atstmt
->missing_ok
= false;
5356 /* Transform the AlterTableStmt */
5357 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5359 context
->queryString
,
5363 /* Execute any statements that should happen before these subcommand(s) */
5364 foreach(lc
, beforeStmts
)
5366 Node
*stmt
= (Node
*) lfirst(lc
);
5368 ProcessUtilityForAlterTable(stmt
, context
);
5369 CommandCounterIncrement();
5372 /* Examine the transformed subcommands and schedule them appropriately */
5373 foreach(lc
, atstmt
->cmds
)
5375 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5379 * This switch need only cover the subcommand types that can be added
5380 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5381 * executing the subcommand immediately, as a substitute for the
5382 * original subcommand. (Note, however, that this does cause
5383 * AT_AddConstraint subcommands to be rescheduled into later passes,
5384 * which is important for index and foreign key constraints.)
5386 * We assume we needn't do any phase-1 checks for added subcommands.
5388 switch (cmd2
->subtype
)
5391 /* Need command-specific recursion decision */
5392 ATPrepSetNotNull(wqueue
, rel
, cmd2
,
5395 pass
= AT_PASS_COL_ATTRS
;
5398 /* This command never recurses */
5399 /* No command-specific prep needed */
5400 pass
= AT_PASS_ADD_INDEX
;
5402 case AT_AddIndexConstraint
:
5403 /* This command never recurses */
5404 /* No command-specific prep needed */
5405 pass
= AT_PASS_ADD_INDEXCONSTR
;
5407 case AT_AddConstraint
:
5408 /* Recursion occurs during execution phase */
5410 cmd2
->recurse
= true;
5411 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5413 case CONSTR_PRIMARY
:
5415 case CONSTR_EXCLUSION
:
5416 pass
= AT_PASS_ADD_INDEXCONSTR
;
5419 pass
= AT_PASS_ADD_OTHERCONSTR
;
5423 case AT_AlterColumnGenericOptions
:
5424 /* This command never recurses */
5425 /* No command-specific prep needed */
5426 pass
= AT_PASS_MISC
;
5433 if (pass
< cur_pass
)
5435 /* Cannot schedule into a pass we already finished */
5436 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5439 else if (pass
> cur_pass
)
5441 /* OK, queue it up for later */
5442 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5447 * We should see at most one subcommand for the current pass,
5448 * which is the transformed version of the original subcommand.
5450 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5452 /* Found the transformed version of our subcommand */
5456 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5461 /* Queue up any after-statements to happen at the end */
5462 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5468 * ATRewriteTables: ALTER TABLE phase 3
5471 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5472 AlterTableUtilityContext
*context
)
5476 /* Go through each table that needs to be checked or rewritten */
5477 foreach(ltab
, *wqueue
)
5479 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5481 /* Relations without storage may be ignored here */
5482 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5486 * If we change column data types, the operation has to be propagated
5487 * to tables that use this table's rowtype as a column type.
5488 * tab->newvals will also be non-NULL in the case where we're adding a
5489 * column with a default. We choose to forbid that case as well,
5490 * since composite types might eventually support defaults.
5492 * (Eventually we'll probably need to check for composite type
5493 * dependencies even when we're just scanning the table without a
5494 * rewrite, but at the moment a composite type does not enforce any
5495 * constraints, so it's not necessary/appropriate to enforce them just
5498 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5502 rel
= table_open(tab
->relid
, NoLock
);
5503 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5504 table_close(rel
, NoLock
);
5508 * We only need to rewrite the table if at least one column needs to
5509 * be recomputed, or we are changing its persistence or access method.
5511 * There are two reasons for requiring a rewrite when changing
5512 * persistence: on one hand, we need to ensure that the buffers
5513 * belonging to each of the two relations are marked with or without
5514 * BM_PERMANENT properly. On the other hand, since rewriting creates
5515 * and assigns a new relfilenumber, we automatically create or drop an
5516 * init fork for the relation as appropriate.
5518 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5520 /* Build a temporary relation and copy data */
5523 Oid NewAccessMethod
;
5527 OldHeap
= table_open(tab
->relid
, NoLock
);
5530 * We don't support rewriting of system catalogs; there are too
5531 * many corner cases and too little benefit. In particular this
5532 * is certainly not going to work for mapped catalogs.
5534 if (IsSystemRelation(OldHeap
))
5536 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5537 errmsg("cannot rewrite system relation \"%s\"",
5538 RelationGetRelationName(OldHeap
))));
5540 if (RelationIsUsedAsCatalogTable(OldHeap
))
5542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5543 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5544 RelationGetRelationName(OldHeap
))));
5547 * Don't allow rewrite on temp tables of other backends ... their
5548 * local buffer manager is not going to cope.
5550 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5553 errmsg("cannot rewrite temporary tables of other sessions")));
5556 * Select destination tablespace (same as original unless user
5557 * requested a change)
5559 if (tab
->newTableSpace
)
5560 NewTableSpace
= tab
->newTableSpace
;
5562 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5565 * Select destination access method (same as original unless user
5566 * requested a change)
5568 if (OidIsValid(tab
->newAccessMethod
))
5569 NewAccessMethod
= tab
->newAccessMethod
;
5571 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5574 * Select persistence of transient table (same as original unless
5575 * user requested a change)
5577 persistence
= tab
->chgPersistence
?
5578 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5580 table_close(OldHeap
, NoLock
);
5583 * Fire off an Event Trigger now, before actually rewriting the
5586 * We don't support Event Trigger for nested commands anywhere,
5587 * here included, and parsetree is given NULL when coming from
5588 * AlterTableInternal.
5590 * And fire it only once.
5593 EventTriggerTableRewrite((Node
*) parsetree
,
5598 * Create transient table that will receive the modified data.
5600 * Ensure it is marked correctly as logged or unlogged. We have
5601 * to do this here so that buffers for the new relfilenumber will
5602 * have the right persistence set, and at the same time ensure
5603 * that the original filenumbers's buffers will get read in with
5604 * the correct setting (i.e. the original one). Otherwise a
5605 * rollback after the rewrite would possibly result with buffers
5606 * for the original filenumbers having the wrong persistence
5609 * NB: This relies on swap_relation_files() also swapping the
5610 * persistence. That wouldn't work for pg_class, but that can't be
5613 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5614 persistence
, lockmode
);
5617 * Copy the heap data into the new table with the desired
5618 * modifications, and test the current data within the table
5619 * against new constraints generated by ALTER TABLE commands.
5621 ATRewriteTable(tab
, OIDNewHeap
, lockmode
);
5624 * Swap the physical files of the old and new heaps, then rebuild
5625 * indexes and discard the old heap. We can use RecentXmin for
5626 * the table's new relfrozenxid because we rewrote all the tuples
5627 * in ATRewriteTable, so no older Xid remains in the table. Also,
5628 * we never try to swap toast tables by content, since we have no
5629 * interest in letting this code work on system catalogs.
5631 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5633 !OidIsValid(tab
->newTableSpace
),
5635 ReadNextMultiXactId(),
5638 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5640 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5642 if (tab
->chgPersistence
)
5643 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5648 * If required, test the current data within the table against new
5649 * constraints generated by ALTER TABLE commands, but don't
5652 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5653 tab
->partition_constraint
!= NULL
)
5654 ATRewriteTable(tab
, InvalidOid
, lockmode
);
5657 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5658 * just do a block-by-block copy.
5660 if (tab
->newTableSpace
)
5661 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5665 * Also change persistence of owned sequences, so that it matches the
5666 * table persistence.
5668 if (tab
->chgPersistence
)
5670 List
*seqlist
= getOwnedSequences(tab
->relid
);
5673 foreach(lc
, seqlist
)
5675 Oid seq_relid
= lfirst_oid(lc
);
5677 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5683 * Foreign key constraints are checked in a final pass, since (a) it's
5684 * generally best to examine each one separately, and (b) it's at least
5685 * theoretically possible that we have changed both relations of the
5686 * foreign key, and we'd better have finished both rewrites before we try
5687 * to read the tables.
5689 foreach(ltab
, *wqueue
)
5691 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5692 Relation rel
= NULL
;
5695 /* Relations without storage may be ignored here too */
5696 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5699 foreach(lcon
, tab
->constraints
)
5701 NewConstraint
*con
= lfirst(lcon
);
5703 if (con
->contype
== CONSTR_FOREIGN
)
5705 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
5710 /* Long since locked, no need for another */
5711 rel
= table_open(tab
->relid
, NoLock
);
5714 refrel
= table_open(con
->refrelid
, RowShareLock
);
5716 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
5721 * No need to mark the constraint row as validated, we did
5722 * that when we inserted the row earlier.
5725 table_close(refrel
, NoLock
);
5730 table_close(rel
, NoLock
);
5733 /* Finally, run any afterStmts that were queued up */
5734 foreach(ltab
, *wqueue
)
5736 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5739 foreach(lc
, tab
->afterStmts
)
5741 Node
*stmt
= (Node
*) lfirst(lc
);
5743 ProcessUtilityForAlterTable(stmt
, context
);
5744 CommandCounterIncrement();
5750 * ATRewriteTable: scan or rewrite one table
5752 * OIDNewHeap is InvalidOid if we don't need to rewrite
5755 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
)
5759 TupleDesc oldTupDesc
;
5760 TupleDesc newTupDesc
;
5761 bool needscan
= false;
5762 List
*notnull_attrs
;
5767 BulkInsertState bistate
;
5769 ExprState
*partqualstate
= NULL
;
5772 * Open the relation(s). We have surely already locked the existing
5775 oldrel
= table_open(tab
->relid
, NoLock
);
5776 oldTupDesc
= tab
->oldDesc
;
5777 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
5779 if (OidIsValid(OIDNewHeap
))
5780 newrel
= table_open(OIDNewHeap
, lockmode
);
5785 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5786 * is empty, so don't bother using it.
5790 mycid
= GetCurrentCommandId(true);
5791 bistate
= GetBulkInsertState();
5792 ti_options
= TABLE_INSERT_SKIP_FSM
;
5796 /* keep compiler quiet about using these uninitialized */
5803 * Generate the constraint and default execution states
5806 estate
= CreateExecutorState();
5808 /* Build the needed expression execution states */
5809 foreach(l
, tab
->constraints
)
5811 NewConstraint
*con
= lfirst(l
);
5813 switch (con
->contype
)
5817 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
5819 case CONSTR_FOREIGN
:
5820 /* Nothing to do here */
5823 elog(ERROR
, "unrecognized constraint type: %d",
5824 (int) con
->contype
);
5828 /* Build expression execution states for partition check quals */
5829 if (tab
->partition_constraint
)
5832 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
5835 foreach(l
, tab
->newvals
)
5837 NewColumnValue
*ex
= lfirst(l
);
5839 /* expr already planned */
5840 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
5843 notnull_attrs
= NIL
;
5844 if (newrel
|| tab
->verify_new_notnull
)
5847 * If we are rebuilding the tuples OR if we added any new but not
5848 * verified NOT NULL constraints, check all not-null constraints. This
5849 * is a bit of overkill but it minimizes risk of bugs, and
5850 * heap_attisnull is a pretty cheap test anyway.
5852 for (i
= 0; i
< newTupDesc
->natts
; i
++)
5854 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
5856 if (attr
->attnotnull
&& !attr
->attisdropped
)
5857 notnull_attrs
= lappend_int(notnull_attrs
, i
);
5863 if (newrel
|| needscan
)
5865 ExprContext
*econtext
;
5866 TupleTableSlot
*oldslot
;
5867 TupleTableSlot
*newslot
;
5869 MemoryContext oldCxt
;
5870 List
*dropped_attrs
= NIL
;
5876 (errmsg_internal("rewriting table \"%s\"",
5877 RelationGetRelationName(oldrel
))));
5880 (errmsg_internal("verifying table \"%s\"",
5881 RelationGetRelationName(oldrel
))));
5886 * All predicate locks on the tuples or pages are about to be made
5887 * invalid, because we move tuples around. Promote them to
5890 TransferPredicateLocksToHeapRelation(oldrel
);
5893 econtext
= GetPerTupleExprContext(estate
);
5896 * Create necessary tuple slots. When rewriting, two slots are needed,
5897 * otherwise one suffices. In the case where one slot suffices, we
5898 * need to use the new tuple descriptor, otherwise some constraints
5899 * can't be evaluated. Note that even when the tuple layout is the
5900 * same and no rewrite is required, the tupDescs might not be
5901 * (consider ADD COLUMN without a default).
5905 Assert(newrel
!= NULL
);
5906 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
5907 table_slot_callbacks(oldrel
));
5908 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
5909 table_slot_callbacks(newrel
));
5912 * Set all columns in the new slot to NULL initially, to ensure
5913 * columns added as part of the rewrite are initialized to NULL.
5914 * That is necessary as tab->newvals will not contain an
5915 * expression for columns with a NULL default, e.g. when adding a
5916 * column without a default together with a column with a default
5917 * requiring an actual rewrite.
5919 ExecStoreAllNullTuple(newslot
);
5923 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
5924 table_slot_callbacks(oldrel
));
5929 * Any attributes that are dropped according to the new tuple
5930 * descriptor can be set to NULL. We precompute the list of dropped
5931 * attributes to avoid needing to do so in the per-tuple loop.
5933 for (i
= 0; i
< newTupDesc
->natts
; i
++)
5935 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
5936 dropped_attrs
= lappend_int(dropped_attrs
, i
);
5940 * Scan through the rows, generating a new row if needed and then
5941 * checking all the constraints.
5943 snapshot
= RegisterSnapshot(GetLatestSnapshot());
5944 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
5947 * Switch to per-tuple memory context and reset it for each tuple
5948 * produced, so we don't leak memory.
5950 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
5952 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
5954 TupleTableSlot
*insertslot
;
5956 if (tab
->rewrite
> 0)
5958 /* Extract data from old tuple */
5959 slot_getallattrs(oldslot
);
5960 ExecClearTuple(newslot
);
5962 /* copy attributes */
5963 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
5964 sizeof(Datum
) * oldslot
->tts_nvalid
);
5965 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
5966 sizeof(bool) * oldslot
->tts_nvalid
);
5968 /* Set dropped attributes to null in new tuple */
5969 foreach(lc
, dropped_attrs
)
5970 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
5973 * Constraints and GENERATED expressions might reference the
5974 * tableoid column, so fill tts_tableOid with the desired
5975 * value. (We must do this each time, because it gets
5976 * overwritten with newrel's OID during storing.)
5978 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
5981 * Process supplied expressions to replace selected columns.
5983 * First, evaluate expressions whose inputs come from the old
5986 econtext
->ecxt_scantuple
= oldslot
;
5988 foreach(l
, tab
->newvals
)
5990 NewColumnValue
*ex
= lfirst(l
);
5992 if (ex
->is_generated
)
5995 newslot
->tts_values
[ex
->attnum
- 1]
5996 = ExecEvalExpr(ex
->exprstate
,
5998 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6001 ExecStoreVirtualTuple(newslot
);
6004 * Now, evaluate any expressions whose inputs come from the
6005 * new tuple. We assume these columns won't reference each
6006 * other, so that there's no ordering dependency.
6008 econtext
->ecxt_scantuple
= newslot
;
6010 foreach(l
, tab
->newvals
)
6012 NewColumnValue
*ex
= lfirst(l
);
6014 if (!ex
->is_generated
)
6017 newslot
->tts_values
[ex
->attnum
- 1]
6018 = ExecEvalExpr(ex
->exprstate
,
6020 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6023 insertslot
= newslot
;
6028 * If there's no rewrite, old and new table are guaranteed to
6029 * have the same AM, so we can just use the old slot to verify
6030 * new constraints etc.
6032 insertslot
= oldslot
;
6035 /* Now check any constraints on the possibly-changed tuple */
6036 econtext
->ecxt_scantuple
= insertslot
;
6038 foreach(l
, notnull_attrs
)
6040 int attn
= lfirst_int(l
);
6042 if (slot_attisnull(insertslot
, attn
+ 1))
6044 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6047 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6048 errmsg("column \"%s\" of relation \"%s\" contains null values",
6049 NameStr(attr
->attname
),
6050 RelationGetRelationName(oldrel
)),
6051 errtablecol(oldrel
, attn
+ 1)));
6055 foreach(l
, tab
->constraints
)
6057 NewConstraint
*con
= lfirst(l
);
6059 switch (con
->contype
)
6062 if (!ExecCheck(con
->qualstate
, econtext
))
6064 (errcode(ERRCODE_CHECK_VIOLATION
),
6065 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6067 RelationGetRelationName(oldrel
)),
6068 errtableconstraint(oldrel
, con
->name
)));
6070 case CONSTR_FOREIGN
:
6071 /* Nothing to do here */
6074 elog(ERROR
, "unrecognized constraint type: %d",
6075 (int) con
->contype
);
6079 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6081 if (tab
->validate_default
)
6083 (errcode(ERRCODE_CHECK_VIOLATION
),
6084 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6085 RelationGetRelationName(oldrel
)),
6089 (errcode(ERRCODE_CHECK_VIOLATION
),
6090 errmsg("partition constraint of relation \"%s\" is violated by some row",
6091 RelationGetRelationName(oldrel
)),
6095 /* Write the tuple out to the new relation */
6097 table_tuple_insert(newrel
, insertslot
, mycid
,
6098 ti_options
, bistate
);
6100 ResetExprContext(econtext
);
6102 CHECK_FOR_INTERRUPTS();
6105 MemoryContextSwitchTo(oldCxt
);
6106 table_endscan(scan
);
6107 UnregisterSnapshot(snapshot
);
6109 ExecDropSingleTupleTableSlot(oldslot
);
6111 ExecDropSingleTupleTableSlot(newslot
);
6114 FreeExecutorState(estate
);
6116 table_close(oldrel
, NoLock
);
6119 FreeBulkInsertState(bistate
);
6121 table_finish_bulk_insert(newrel
, ti_options
);
6123 table_close(newrel
, NoLock
);
6128 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6130 static AlteredTableInfo
*
6131 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6133 Oid relid
= RelationGetRelid(rel
);
6134 AlteredTableInfo
*tab
;
6137 foreach(ltab
, *wqueue
)
6139 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6140 if (tab
->relid
== relid
)
6145 * Not there, so add it. Note that we make a copy of the relation's
6146 * existing descriptor before anything interesting can happen to it.
6148 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6150 tab
->rel
= NULL
; /* set later */
6151 tab
->relkind
= rel
->rd_rel
->relkind
;
6152 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6153 tab
->newAccessMethod
= InvalidOid
;
6154 tab
->newTableSpace
= InvalidOid
;
6155 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6156 tab
->chgPersistence
= false;
6158 *wqueue
= lappend(*wqueue
, tab
);
6164 alter_table_type_to_string(AlterTableType cmdtype
)
6169 case AT_AddColumnToView
:
6170 return "ADD COLUMN";
6171 case AT_ColumnDefault
:
6172 case AT_CookedColumnDefault
:
6173 return "ALTER COLUMN ... SET DEFAULT";
6174 case AT_DropNotNull
:
6175 return "ALTER COLUMN ... DROP NOT NULL";
6177 return "ALTER COLUMN ... SET NOT NULL";
6178 case AT_DropExpression
:
6179 return "ALTER COLUMN ... DROP EXPRESSION";
6180 case AT_CheckNotNull
:
6181 return NULL
; /* not real grammar */
6182 case AT_SetStatistics
:
6183 return "ALTER COLUMN ... SET STATISTICS";
6185 return "ALTER COLUMN ... SET";
6186 case AT_ResetOptions
:
6187 return "ALTER COLUMN ... RESET";
6189 return "ALTER COLUMN ... SET STORAGE";
6190 case AT_SetCompression
:
6191 return "ALTER COLUMN ... SET COMPRESSION";
6193 return "DROP COLUMN";
6196 return NULL
; /* not real grammar */
6197 case AT_AddConstraint
:
6198 case AT_ReAddConstraint
:
6199 case AT_ReAddDomainConstraint
:
6200 case AT_AddIndexConstraint
:
6201 return "ADD CONSTRAINT";
6202 case AT_AlterConstraint
:
6203 return "ALTER CONSTRAINT";
6204 case AT_ValidateConstraint
:
6205 return "VALIDATE CONSTRAINT";
6206 case AT_DropConstraint
:
6207 return "DROP CONSTRAINT";
6208 case AT_ReAddComment
:
6209 return NULL
; /* not real grammar */
6210 case AT_AlterColumnType
:
6211 return "ALTER COLUMN ... SET DATA TYPE";
6212 case AT_AlterColumnGenericOptions
:
6213 return "ALTER COLUMN ... OPTIONS";
6214 case AT_ChangeOwner
:
6217 return "CLUSTER ON";
6218 case AT_DropCluster
:
6219 return "SET WITHOUT CLUSTER";
6220 case AT_SetAccessMethod
:
6221 return "SET ACCESS METHOD";
6223 return "SET LOGGED";
6224 case AT_SetUnLogged
:
6225 return "SET UNLOGGED";
6227 return "SET WITHOUT OIDS";
6228 case AT_SetTableSpace
:
6229 return "SET TABLESPACE";
6230 case AT_SetRelOptions
:
6232 case AT_ResetRelOptions
:
6234 case AT_ReplaceRelOptions
:
6235 return NULL
; /* not real grammar */
6237 return "ENABLE TRIGGER";
6238 case AT_EnableAlwaysTrig
:
6239 return "ENABLE ALWAYS TRIGGER";
6240 case AT_EnableReplicaTrig
:
6241 return "ENABLE REPLICA TRIGGER";
6242 case AT_DisableTrig
:
6243 return "DISABLE TRIGGER";
6244 case AT_EnableTrigAll
:
6245 return "ENABLE TRIGGER ALL";
6246 case AT_DisableTrigAll
:
6247 return "DISABLE TRIGGER ALL";
6248 case AT_EnableTrigUser
:
6249 return "ENABLE TRIGGER USER";
6250 case AT_DisableTrigUser
:
6251 return "DISABLE TRIGGER USER";
6253 return "ENABLE RULE";
6254 case AT_EnableAlwaysRule
:
6255 return "ENABLE ALWAYS RULE";
6256 case AT_EnableReplicaRule
:
6257 return "ENABLE REPLICA RULE";
6258 case AT_DisableRule
:
6259 return "DISABLE RULE";
6262 case AT_DropInherit
:
6263 return "NO INHERIT";
6268 case AT_ReplicaIdentity
:
6269 return "REPLICA IDENTITY";
6270 case AT_EnableRowSecurity
:
6271 return "ENABLE ROW SECURITY";
6272 case AT_DisableRowSecurity
:
6273 return "DISABLE ROW SECURITY";
6274 case AT_ForceRowSecurity
:
6275 return "FORCE ROW SECURITY";
6276 case AT_NoForceRowSecurity
:
6277 return "NO FORCE ROW SECURITY";
6278 case AT_GenericOptions
:
6280 case AT_AttachPartition
:
6281 return "ATTACH PARTITION";
6282 case AT_DetachPartition
:
6283 return "DETACH PARTITION";
6284 case AT_DetachPartitionFinalize
:
6285 return "DETACH PARTITION ... FINALIZE";
6286 case AT_AddIdentity
:
6287 return "ALTER COLUMN ... ADD IDENTITY";
6288 case AT_SetIdentity
:
6289 return "ALTER COLUMN ... SET";
6290 case AT_DropIdentity
:
6291 return "ALTER COLUMN ... DROP IDENTITY";
6292 case AT_ReAddStatistics
:
6293 return NULL
; /* not real grammar */
6300 * ATSimplePermissions
6302 * - Ensure that it is a relation (or possibly a view)
6303 * - Ensure this user is the owner
6304 * - Ensure that it is not a system table
6307 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6311 switch (rel
->rd_rel
->relkind
)
6313 case RELKIND_RELATION
:
6314 case RELKIND_PARTITIONED_TABLE
:
6315 actual_target
= ATT_TABLE
;
6318 actual_target
= ATT_VIEW
;
6320 case RELKIND_MATVIEW
:
6321 actual_target
= ATT_MATVIEW
;
6324 actual_target
= ATT_INDEX
;
6326 case RELKIND_PARTITIONED_INDEX
:
6327 actual_target
= ATT_PARTITIONED_INDEX
;
6329 case RELKIND_COMPOSITE_TYPE
:
6330 actual_target
= ATT_COMPOSITE_TYPE
;
6332 case RELKIND_FOREIGN_TABLE
:
6333 actual_target
= ATT_FOREIGN_TABLE
;
6335 case RELKIND_SEQUENCE
:
6336 actual_target
= ATT_SEQUENCE
;
6343 /* Wrong target type? */
6344 if ((actual_target
& allowed_targets
) == 0)
6346 const char *action_str
= alter_table_type_to_string(cmdtype
);
6350 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6351 /* translator: %s is a group of some SQL keywords */
6352 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6353 action_str
, RelationGetRelationName(rel
)),
6354 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6356 /* internal error? */
6357 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6358 RelationGetRelationName(rel
));
6361 /* Permissions checks */
6362 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6363 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6364 RelationGetRelationName(rel
));
6366 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6368 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6369 errmsg("permission denied: \"%s\" is a system catalog",
6370 RelationGetRelationName(rel
))));
6376 * Simple table recursion sufficient for most ALTER TABLE operations.
6377 * All direct and indirect children are processed in an unspecified order.
6378 * Note that if a child inherits from the original table via multiple
6379 * inheritance paths, it will be visited just once.
6382 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6383 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6384 AlterTableUtilityContext
*context
)
6387 * Propagate to children, if desired and if there are (or might be) any
6390 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6392 Oid relid
= RelationGetRelid(rel
);
6396 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6399 * find_all_inheritors does the recursive search of the inheritance
6400 * hierarchy, so all we have to do is process all of the relids in the
6401 * list that it returns.
6403 foreach(child
, children
)
6405 Oid childrelid
= lfirst_oid(child
);
6408 if (childrelid
== relid
)
6410 /* find_all_inheritors already got lock */
6411 childrel
= relation_open(childrelid
, NoLock
);
6412 CheckTableNotInUse(childrel
, "ALTER TABLE");
6413 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6414 relation_close(childrel
, NoLock
);
6420 * Obtain list of partitions of the given table, locking them all at the given
6421 * lockmode and ensuring that they all pass CheckTableNotInUse.
6423 * This function is a no-op if the given relation is not a partitioned table;
6424 * in particular, nothing is done if it's a legacy inheritance parent.
6427 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6429 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6434 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6435 /* first element is the parent rel; must ignore it */
6436 for_each_from(cell
, inh
, 1)
6440 /* find_all_inheritors already got lock */
6441 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6442 CheckTableNotInUse(childrel
, "ALTER TABLE");
6443 table_close(childrel
, NoLock
);
6450 * ATTypedTableRecursion
6452 * Propagate ALTER TYPE operations to the typed tables of that type.
6453 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6454 * recursion to inheritance children of the typed tables.
6457 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6458 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6463 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6465 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6466 RelationGetRelationName(rel
),
6469 foreach(child
, children
)
6471 Oid childrelid
= lfirst_oid(child
);
6474 childrel
= relation_open(childrelid
, lockmode
);
6475 CheckTableNotInUse(childrel
, "ALTER TABLE");
6476 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6477 relation_close(childrel
, NoLock
);
6483 * find_composite_type_dependencies
6485 * Check to see if the type "typeOid" is being used as a column in some table
6486 * (possibly nested several levels deep in composite types, arrays, etc!).
6487 * Eventually, we'd like to propagate the check or rewrite operation
6488 * into such tables, but for now, just error out if we find any.
6490 * Caller should provide either the associated relation of a rowtype,
6491 * or a type name (not both) for use in the error message, if any.
6493 * Note that "typeOid" is not necessarily a composite type; it could also be
6494 * another container type such as an array or range, or a domain over one of
6495 * these things. The name of this function is therefore somewhat historical,
6496 * but it's not worth changing.
6498 * We assume that functions and views depending on the type are not reasons
6499 * to reject the ALTER. (How safe is this really?)
6502 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6503 const char *origTypeName
)
6507 SysScanDesc depScan
;
6510 /* since this function recurses, it could be driven to stack overflow */
6511 check_stack_depth();
6514 * We scan pg_depend to find those things that depend on the given type.
6515 * (We assume we can ignore refobjsubid for a type.)
6517 depRel
= table_open(DependRelationId
, AccessShareLock
);
6519 ScanKeyInit(&key
[0],
6520 Anum_pg_depend_refclassid
,
6521 BTEqualStrategyNumber
, F_OIDEQ
,
6522 ObjectIdGetDatum(TypeRelationId
));
6523 ScanKeyInit(&key
[1],
6524 Anum_pg_depend_refobjid
,
6525 BTEqualStrategyNumber
, F_OIDEQ
,
6526 ObjectIdGetDatum(typeOid
));
6528 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6531 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6533 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6535 TupleDesc tupleDesc
;
6536 Form_pg_attribute att
;
6538 /* Check for directly dependent types */
6539 if (pg_depend
->classid
== TypeRelationId
)
6542 * This must be an array, domain, or range containing the given
6543 * type, so recursively check for uses of this type. Note that
6544 * any error message will mention the original type not the
6545 * container; this is intentional.
6547 find_composite_type_dependencies(pg_depend
->objid
,
6548 origRelation
, origTypeName
);
6552 /* Else, ignore dependees that aren't relations */
6553 if (pg_depend
->classid
!= RelationRelationId
)
6556 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6557 tupleDesc
= RelationGetDescr(rel
);
6560 * If objsubid identifies a specific column, refer to that in error
6561 * messages. Otherwise, search to see if there's a user column of the
6562 * type. (We assume system columns are never of interesting types.)
6563 * The search is needed because an index containing an expression
6564 * column of the target type will just be recorded as a whole-relation
6565 * dependency. If we do not find a column of the type, the dependency
6566 * must indicate that the type is transiently referenced in an index
6567 * expression but not stored on disk, which we assume is OK, just as
6568 * we do for references in views. (It could also be that the target
6569 * type is embedded in some container type that is stored in an index
6570 * column, but the previous recursion should catch such cases.)
6572 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6573 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6577 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6579 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6580 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6586 /* No such column, so assume OK */
6587 relation_close(rel
, AccessShareLock
);
6593 * We definitely should reject if the relation has storage. If it's
6594 * partitioned, then perhaps we don't have to reject: if there are
6595 * partitions then we'll fail when we find one, else there is no
6596 * stored data to worry about. However, it's possible that the type
6597 * change would affect conclusions about whether the type is sortable
6598 * or hashable and thus (if it's a partitioning column) break the
6599 * partitioning rule. For now, reject for partitioned rels too.
6601 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6602 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6606 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6607 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6609 RelationGetRelationName(rel
),
6610 NameStr(att
->attname
))));
6611 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6613 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6614 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6615 RelationGetRelationName(origRelation
),
6616 RelationGetRelationName(rel
),
6617 NameStr(att
->attname
))));
6618 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6620 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6621 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6622 RelationGetRelationName(origRelation
),
6623 RelationGetRelationName(rel
),
6624 NameStr(att
->attname
))));
6627 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6628 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6629 RelationGetRelationName(origRelation
),
6630 RelationGetRelationName(rel
),
6631 NameStr(att
->attname
))));
6633 else if (OidIsValid(rel
->rd_rel
->reltype
))
6636 * A view or composite type itself isn't a problem, but we must
6637 * recursively check for indirect dependencies via its rowtype.
6639 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6640 origRelation
, origTypeName
);
6643 relation_close(rel
, AccessShareLock
);
6646 systable_endscan(depScan
);
6648 relation_close(depRel
, AccessShareLock
);
6653 * find_typed_table_dependencies
6655 * Check to see if a composite type is being used as the type of a
6656 * typed table. Abort if any are found and behavior is RESTRICT.
6657 * Else return the list of tables.
6660 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6668 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6670 ScanKeyInit(&key
[0],
6671 Anum_pg_class_reloftype
,
6672 BTEqualStrategyNumber
, F_OIDEQ
,
6673 ObjectIdGetDatum(typeOid
));
6675 scan
= table_beginscan_catalog(classRel
, 1, key
);
6677 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6679 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6681 if (behavior
== DROP_RESTRICT
)
6683 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6684 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6686 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6688 result
= lappend_oid(result
, classform
->oid
);
6691 table_endscan(scan
);
6692 table_close(classRel
, AccessShareLock
);
6701 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6702 * isn't suitable, throw an error. Currently, we require that the type
6703 * originated with CREATE TYPE AS. We could support any row type, but doing so
6704 * would require handling a number of extra corner cases in the DDL commands.
6705 * (Also, allowing domain-over-composite would open up a can of worms about
6706 * whether and how the domain's constraints should apply to derived tables.)
6709 check_of_type(HeapTuple typetuple
)
6711 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
6712 bool typeOk
= false;
6714 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
6716 Relation typeRelation
;
6718 Assert(OidIsValid(typ
->typrelid
));
6719 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
6720 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6723 * Close the parent rel, but keep our AccessShareLock on it until xact
6724 * commit. That will prevent someone else from deleting or ALTERing
6725 * the type before the typed table creation/conversion commits.
6727 relation_close(typeRelation
, NoLock
);
6731 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6732 errmsg("type %s is not a composite type",
6733 format_type_be(typ
->oid
))));
6738 * ALTER TABLE ADD COLUMN
6740 * Adds an additional attribute to a relation making the assumption that
6741 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6742 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6745 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6746 * have to decide at runtime whether to recurse or not depending on whether we
6747 * actually add a column or merely merge with an existing column. (We can't
6748 * check this in a static pre-pass because it won't handle multiple inheritance
6749 * situations correctly.)
6752 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
6753 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
6754 AlterTableUtilityContext
*context
)
6756 if (rel
->rd_rel
->reloftype
&& !recursing
)
6758 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6759 errmsg("cannot add column to typed table")));
6761 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6762 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
6764 if (recurse
&& !is_view
)
6765 cmd
->recurse
= true;
6769 * Add a column to a table. The return value is the address of the
6770 * new column in the parent relation.
6772 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6773 * copy (but that happens only after we check for IF NOT EXISTS).
6775 static ObjectAddress
6776 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
6777 AlterTableCmd
**cmd
,
6778 bool recurse
, bool recursing
,
6779 LOCKMODE lockmode
, int cur_pass
,
6780 AlterTableUtilityContext
*context
)
6782 Oid myrelid
= RelationGetRelid(rel
);
6783 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
6784 bool if_not_exists
= (*cmd
)->missing_ok
;
6788 FormData_pg_attribute attribute
;
6791 HeapTuple typeTuple
;
6799 AlterTableCmd
*childcmd
;
6800 AclResult aclresult
;
6801 ObjectAddress address
;
6803 FormData_pg_attribute
*aattr
[] = {&attribute
};
6805 /* At top level, permission check was done in ATPrepCmd, else do it */
6807 ATSimplePermissions((*cmd
)->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
6809 if (rel
->rd_rel
->relispartition
&& !recursing
)
6811 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6812 errmsg("cannot add column to a partition")));
6814 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
6817 * Are we adding the column to a recursion child? If so, check whether to
6818 * merge with an existing definition for the column. If we do merge, we
6819 * must not recurse. Children will already have the column, and recursing
6820 * into them would mess up attinhcount.
6822 if (colDef
->inhcount
> 0)
6826 /* Does child already have a column by this name? */
6827 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
6828 if (HeapTupleIsValid(tuple
))
6830 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
6835 /* Child column must match on type, typmod, and collation */
6836 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
6837 if (ctypeId
!= childatt
->atttypid
||
6838 ctypmod
!= childatt
->atttypmod
)
6840 (errcode(ERRCODE_DATATYPE_MISMATCH
),
6841 errmsg("child table \"%s\" has different type for column \"%s\"",
6842 RelationGetRelationName(rel
), colDef
->colname
)));
6843 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
6844 if (ccollid
!= childatt
->attcollation
)
6846 (errcode(ERRCODE_COLLATION_MISMATCH
),
6847 errmsg("child table \"%s\" has different collation for column \"%s\"",
6848 RelationGetRelationName(rel
), colDef
->colname
),
6849 errdetail("\"%s\" versus \"%s\"",
6850 get_collation_name(ccollid
),
6851 get_collation_name(childatt
->attcollation
))));
6853 /* Bump the existing child att's inhcount */
6854 childatt
->attinhcount
++;
6855 if (childatt
->attinhcount
< 0)
6857 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
6858 errmsg("too many inheritance parents"));
6859 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
6861 heap_freetuple(tuple
);
6863 /* Inform the user about the merge */
6865 (errmsg("merging definition of column \"%s\" for child \"%s\"",
6866 colDef
->colname
, RelationGetRelationName(rel
))));
6868 table_close(attrdesc
, RowExclusiveLock
);
6869 return InvalidObjectAddress
;
6873 /* skip if the name already exists and if_not_exists is true */
6874 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
6876 table_close(attrdesc
, RowExclusiveLock
);
6877 return InvalidObjectAddress
;
6881 * Okay, we need to add the column, so go ahead and do parse
6882 * transformation. This can result in queueing up, or even immediately
6883 * executing, subsidiary operations (such as creation of unique indexes);
6884 * so we mustn't do it until we have made the if_not_exists check.
6886 * When recursing, the command was already transformed and we needn't do
6887 * so again. Also, if context isn't given we can't transform. (That
6888 * currently happens only for AT_AddColumnToView; we expect that view.c
6889 * passed us a ColumnDef that doesn't need work.)
6891 if (context
!= NULL
&& !recursing
)
6893 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
6895 Assert(*cmd
!= NULL
);
6896 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
6900 * Cannot add identity column if table has children, because identity does
6901 * not inherit. (Adding column and identity separately will work.)
6903 if (colDef
->identity
&&
6905 find_inheritance_children(myrelid
, NoLock
) != NIL
)
6907 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
6908 errmsg("cannot recursively add identity column to table that has child tables")));
6910 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
6912 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
6913 if (!HeapTupleIsValid(reltup
))
6914 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
6915 relkind
= ((Form_pg_class
) GETSTRUCT(reltup
))->relkind
;
6917 /* Determine the new attribute's number */
6918 newattnum
= ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
+ 1;
6919 if (newattnum
> MaxHeapAttributeNumber
)
6921 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
6922 errmsg("tables can have at most %d columns",
6923 MaxHeapAttributeNumber
)));
6925 typeTuple
= typenameType(NULL
, colDef
->typeName
, &typmod
);
6926 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
6927 typeOid
= tform
->oid
;
6929 aclresult
= object_aclcheck(TypeRelationId
, typeOid
, GetUserId(), ACL_USAGE
);
6930 if (aclresult
!= ACLCHECK_OK
)
6931 aclcheck_error_type(aclresult
, typeOid
);
6933 collOid
= GetColumnDefCollation(NULL
, colDef
, typeOid
);
6935 /* make sure datatype is legal for a column */
6936 CheckAttributeType(colDef
->colname
, typeOid
, collOid
,
6937 list_make1_oid(rel
->rd_rel
->reltype
),
6941 * Construct new attribute's pg_attribute entry. (Variable-length fields
6942 * are handled by InsertPgAttributeTuples().)
6944 attribute
.attrelid
= myrelid
;
6945 namestrcpy(&(attribute
.attname
), colDef
->colname
);
6946 attribute
.atttypid
= typeOid
;
6947 attribute
.attstattarget
= -1;
6948 attribute
.attlen
= tform
->typlen
;
6949 attribute
.attnum
= newattnum
;
6950 if (list_length(colDef
->typeName
->arrayBounds
) > PG_INT16_MAX
)
6952 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
6953 errmsg("too many array dimensions"));
6954 attribute
.attndims
= list_length(colDef
->typeName
->arrayBounds
);
6955 attribute
.atttypmod
= typmod
;
6956 attribute
.attbyval
= tform
->typbyval
;
6957 attribute
.attalign
= tform
->typalign
;
6958 if (colDef
->storage_name
)
6959 attribute
.attstorage
= GetAttributeStorage(typeOid
, colDef
->storage_name
);
6961 attribute
.attstorage
= tform
->typstorage
;
6962 attribute
.attcompression
= GetAttributeCompression(typeOid
,
6963 colDef
->compression
);
6964 attribute
.attnotnull
= colDef
->is_not_null
;
6965 attribute
.atthasdef
= false;
6966 attribute
.atthasmissing
= false;
6967 attribute
.attidentity
= colDef
->identity
;
6968 attribute
.attgenerated
= colDef
->generated
;
6969 attribute
.attisdropped
= false;
6970 attribute
.attislocal
= colDef
->is_local
;
6971 attribute
.attinhcount
= colDef
->inhcount
;
6972 attribute
.attcollation
= collOid
;
6974 ReleaseSysCache(typeTuple
);
6976 tupdesc
= CreateTupleDesc(lengthof(aattr
), (FormData_pg_attribute
**) &aattr
);
6978 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
6980 table_close(attrdesc
, RowExclusiveLock
);
6983 * Update pg_class tuple as appropriate
6985 ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
= newattnum
;
6987 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
6989 heap_freetuple(reltup
);
6991 /* Post creation hook for new attribute */
6992 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
6994 table_close(pgclass
, RowExclusiveLock
);
6996 /* Make the attribute's catalog entry visible */
6997 CommandCounterIncrement();
7000 * Store the DEFAULT, if any, in the catalogs
7002 if (colDef
->raw_default
)
7004 RawColumnDefault
*rawEnt
;
7006 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7007 rawEnt
->attnum
= attribute
.attnum
;
7008 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7011 * Attempt to skip a complete table rewrite by storing the specified
7012 * DEFAULT value outside of the heap. This may be disabled inside
7013 * AddRelationNewConstraints if the optimization cannot be applied.
7015 rawEnt
->missingMode
= (!colDef
->generated
);
7017 rawEnt
->generated
= colDef
->generated
;
7020 * This function is intended for CREATE TABLE, so it processes a
7021 * _list_ of defaults, but we just do one.
7023 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7024 false, true, false, NULL
);
7026 /* Make the additional catalog changes visible */
7027 CommandCounterIncrement();
7030 * Did the request for a missing value work? If not we'll have to do a
7033 if (!rawEnt
->missingMode
)
7034 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7038 * Tell Phase 3 to fill in the default expression, if there is one.
7040 * If there is no default, Phase 3 doesn't have to do anything, because
7041 * that effectively means that the default is NULL. The heap tuple access
7042 * routines always check for attnum > # of attributes in tuple, and return
7043 * NULL if so, so without any modification of the tuple data we will get
7044 * the effect of NULL values in the new column.
7046 * An exception occurs when the new column is of a domain type: the domain
7047 * might have a NOT NULL constraint, or a check constraint that indirectly
7048 * rejects nulls. If there are any domain constraints then we construct
7049 * an explicit NULL default value that will be passed through
7050 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7051 * rewriting the table which we really don't have to do, but the present
7052 * design of domain processing doesn't offer any simple way of checking
7053 * the constraints more directly.)
7055 * Note: we use build_column_default, and not just the cooked default
7056 * returned by AddRelationNewConstraints, so that the right thing happens
7057 * when a datatype's default applies.
7059 * Note: it might seem that this should happen at the end of Phase 2, so
7060 * that the effects of subsequent subcommands can be taken into account.
7061 * It's intentional that we do it now, though. The new column should be
7062 * filled according to what is said in the ADD COLUMN subcommand, so that
7063 * the effects are the same as if this subcommand had been run by itself
7064 * and the later subcommands had been issued in new ALTER TABLE commands.
7066 * We can skip this entirely for relations without storage, since Phase 3
7067 * is certainly not going to touch them. System attributes don't have
7068 * interesting defaults, either.
7070 if (RELKIND_HAS_STORAGE(relkind
))
7073 * For an identity column, we can't use build_column_default(),
7074 * because the sequence ownership isn't set yet. So do it manually.
7076 if (colDef
->identity
)
7078 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7080 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7081 nve
->typeId
= typeOid
;
7083 defval
= (Expr
*) nve
;
7085 /* must do a rewrite for identity columns */
7086 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7089 defval
= (Expr
*) build_column_default(rel
, attribute
.attnum
);
7091 if (!defval
&& DomainHasConstraints(typeOid
))
7097 baseTypeMod
= typmod
;
7098 baseTypeId
= getBaseTypeAndTypmod(typeOid
, &baseTypeMod
);
7099 baseTypeColl
= get_typcollation(baseTypeId
);
7100 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7101 defval
= (Expr
*) coerce_to_target_type(NULL
,
7106 COERCION_ASSIGNMENT
,
7107 COERCE_IMPLICIT_CAST
,
7109 if (defval
== NULL
) /* should not happen */
7110 elog(ERROR
, "failed to coerce base type to domain");
7115 NewColumnValue
*newval
;
7117 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7118 newval
->attnum
= attribute
.attnum
;
7119 newval
->expr
= expression_planner(defval
);
7120 newval
->is_generated
= (colDef
->generated
!= '\0');
7122 tab
->newvals
= lappend(tab
->newvals
, newval
);
7125 if (DomainHasConstraints(typeOid
))
7126 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7128 if (!TupleDescAttr(rel
->rd_att
, attribute
.attnum
- 1)->atthasmissing
)
7131 * If the new column is NOT NULL, and there is no missing value,
7132 * tell Phase 3 it needs to check for NULLs.
7134 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7139 * Add needed dependency entries for the new column.
7141 add_column_datatype_dependency(myrelid
, newattnum
, attribute
.atttypid
);
7142 add_column_collation_dependency(myrelid
, newattnum
, attribute
.attcollation
);
7145 * Propagate to children as appropriate. Unlike most other ALTER
7146 * routines, we have to do this one level of recursion at a time; we can't
7147 * use find_all_inheritors to do it in one pass.
7150 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7153 * If we are told not to recurse, there had better not be any child
7154 * tables; else the addition would put them out of step.
7156 if (children
&& !recurse
)
7158 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7159 errmsg("column must be added to child tables too")));
7161 /* Children should see column as singly inherited */
7164 childcmd
= copyObject(*cmd
);
7165 colDef
= castNode(ColumnDef
, childcmd
->def
);
7166 colDef
->inhcount
= 1;
7167 colDef
->is_local
= false;
7170 childcmd
= *cmd
; /* no need to copy again */
7172 foreach(child
, children
)
7174 Oid childrelid
= lfirst_oid(child
);
7176 AlteredTableInfo
*childtab
;
7178 /* find_inheritance_children already got lock */
7179 childrel
= table_open(childrelid
, NoLock
);
7180 CheckTableNotInUse(childrel
, "ALTER TABLE");
7182 /* Find or create work queue entry for this table */
7183 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7185 /* Recurse to child; return value is ignored */
7186 ATExecAddColumn(wqueue
, childtab
, childrel
,
7187 &childcmd
, recurse
, true,
7188 lockmode
, cur_pass
, context
);
7190 table_close(childrel
, NoLock
);
7193 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7198 * If a new or renamed column will collide with the name of an existing
7199 * column and if_not_exists is false then error out, else do nothing.
7202 check_for_column_name_collision(Relation rel
, const char *colname
,
7209 * this test is deliberately not attisdropped-aware, since if one tries to
7210 * add a column matching a dropped column name, it's gonna fail anyway.
7212 attTuple
= SearchSysCache2(ATTNAME
,
7213 ObjectIdGetDatum(RelationGetRelid(rel
)),
7214 PointerGetDatum(colname
));
7215 if (!HeapTupleIsValid(attTuple
))
7218 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7219 ReleaseSysCache(attTuple
);
7222 * We throw a different error message for conflicts with system column
7223 * names, since they are normally not shown and the user might otherwise
7224 * be confused about the reason for the conflict.
7228 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7229 errmsg("column name \"%s\" conflicts with a system column name",
7236 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7237 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7238 colname
, RelationGetRelationName(rel
))));
7243 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7244 errmsg("column \"%s\" of relation \"%s\" already exists",
7245 colname
, RelationGetRelationName(rel
))));
7252 * Install a column's dependency on its datatype.
7255 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7257 ObjectAddress myself
,
7260 myself
.classId
= RelationRelationId
;
7261 myself
.objectId
= relid
;
7262 myself
.objectSubId
= attnum
;
7263 referenced
.classId
= TypeRelationId
;
7264 referenced
.objectId
= typid
;
7265 referenced
.objectSubId
= 0;
7266 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7270 * Install a column's dependency on its collation.
7273 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7275 ObjectAddress myself
,
7278 /* We know the default collation is pinned, so don't bother recording it */
7279 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7281 myself
.classId
= RelationRelationId
;
7282 myself
.objectId
= relid
;
7283 myself
.objectSubId
= attnum
;
7284 referenced
.classId
= CollationRelationId
;
7285 referenced
.objectId
= collid
;
7286 referenced
.objectSubId
= 0;
7287 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7292 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7296 ATPrepDropNotNull(Relation rel
, bool recurse
, bool recursing
)
7299 * If the parent is a partitioned table, like check constraints, we do not
7300 * support removing the NOT NULL while partitions exist.
7302 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7304 PartitionDesc partdesc
= RelationGetPartitionDesc(rel
, true);
7306 Assert(partdesc
!= NULL
);
7307 if (partdesc
->nparts
> 0 && !recurse
&& !recursing
)
7309 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7310 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7311 errhint("Do not specify the ONLY keyword.")));
7316 * Return the address of the modified column. If the column was already
7317 * nullable, InvalidObjectAddress is returned.
7319 static ObjectAddress
7320 ATExecDropNotNull(Relation rel
, const char *colName
, LOCKMODE lockmode
)
7323 Form_pg_attribute attTup
;
7327 ListCell
*indexoidscan
;
7328 ObjectAddress address
;
7331 * lookup the attribute
7333 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7335 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7336 if (!HeapTupleIsValid(tuple
))
7338 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7339 errmsg("column \"%s\" of relation \"%s\" does not exist",
7340 colName
, RelationGetRelationName(rel
))));
7341 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7342 attnum
= attTup
->attnum
;
7344 /* Prevent them from altering a system attribute */
7347 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7348 errmsg("cannot alter system column \"%s\"",
7351 if (attTup
->attidentity
)
7353 (errcode(ERRCODE_SYNTAX_ERROR
),
7354 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7355 colName
, RelationGetRelationName(rel
))));
7358 * Check that the attribute is not in a primary key or in an index used as
7359 * a replica identity.
7361 * Note: we'll throw error even if the pkey index is not valid.
7364 /* Loop over all indexes on the relation */
7365 indexoidlist
= RelationGetIndexList(rel
);
7367 foreach(indexoidscan
, indexoidlist
)
7369 Oid indexoid
= lfirst_oid(indexoidscan
);
7370 HeapTuple indexTuple
;
7371 Form_pg_index indexStruct
;
7374 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
7375 if (!HeapTupleIsValid(indexTuple
))
7376 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
7377 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
7380 * If the index is not a primary key or an index used as replica
7381 * identity, skip the check.
7383 if (indexStruct
->indisprimary
|| indexStruct
->indisreplident
)
7386 * Loop over each attribute in the primary key or the index used
7387 * as replica identity and see if it matches the to-be-altered
7390 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
7392 if (indexStruct
->indkey
.values
[i
] == attnum
)
7394 if (indexStruct
->indisprimary
)
7396 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7397 errmsg("column \"%s\" is in a primary key",
7401 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7402 errmsg("column \"%s\" is in index used as replica identity",
7408 ReleaseSysCache(indexTuple
);
7411 list_free(indexoidlist
);
7413 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7414 if (rel
->rd_rel
->relispartition
)
7416 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7417 Relation parent
= table_open(parentId
, AccessShareLock
);
7418 TupleDesc tupDesc
= RelationGetDescr(parent
);
7419 AttrNumber parent_attnum
;
7421 parent_attnum
= get_attnum(parentId
, colName
);
7422 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7424 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7425 errmsg("column \"%s\" is marked NOT NULL in parent table",
7427 table_close(parent
, AccessShareLock
);
7431 * Okay, actually perform the catalog change ... if needed
7433 if (attTup
->attnotnull
)
7435 attTup
->attnotnull
= false;
7437 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7439 ObjectAddressSubSet(address
, RelationRelationId
,
7440 RelationGetRelid(rel
), attnum
);
7443 address
= InvalidObjectAddress
;
7445 InvokeObjectPostAlterHook(RelationRelationId
,
7446 RelationGetRelid(rel
), attnum
);
7448 table_close(attr_rel
, RowExclusiveLock
);
7454 * ALTER TABLE ALTER COLUMN SET NOT NULL
7458 ATPrepSetNotNull(List
**wqueue
, Relation rel
,
7459 AlterTableCmd
*cmd
, bool recurse
, bool recursing
,
7460 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
7463 * If we're already recursing, there's nothing to do; the topmost
7464 * invocation of ATSimpleRecursion already visited all children.
7470 * If the target column is already marked NOT NULL, we can skip recursing
7471 * to children, because their columns should already be marked NOT NULL as
7472 * well. But there's no point in checking here unless the relation has
7473 * some children; else we can just wait till execution to check. (If it
7474 * does have children, however, this can save taking per-child locks
7475 * unnecessarily. This greatly improves concurrency in some parallel
7476 * restore scenarios.)
7478 * Unfortunately, we can only apply this optimization to partitioned
7479 * tables, because traditional inheritance doesn't enforce that child
7480 * columns be NOT NULL when their parent is. (That's a bug that should
7481 * get fixed someday.)
7483 if (rel
->rd_rel
->relhassubclass
&&
7484 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7489 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), cmd
->name
);
7491 /* Might as well throw the error now, if name is bad */
7492 if (!HeapTupleIsValid(tuple
))
7494 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7495 errmsg("column \"%s\" of relation \"%s\" does not exist",
7496 cmd
->name
, RelationGetRelationName(rel
))));
7498 attnotnull
= ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
;
7499 ReleaseSysCache(tuple
);
7505 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7506 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7507 * normal recursion logic.
7509 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
7512 AlterTableCmd
*newcmd
= makeNode(AlterTableCmd
);
7514 newcmd
->subtype
= AT_CheckNotNull
;
7515 newcmd
->name
= pstrdup(cmd
->name
);
7516 ATSimpleRecursion(wqueue
, rel
, newcmd
, true, lockmode
, context
);
7519 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
7523 * Return the address of the modified column. If the column was already NOT
7524 * NULL, InvalidObjectAddress is returned.
7526 static ObjectAddress
7527 ATExecSetNotNull(AlteredTableInfo
*tab
, Relation rel
,
7528 const char *colName
, LOCKMODE lockmode
)
7533 ObjectAddress address
;
7536 * lookup the attribute
7538 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7540 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7542 if (!HeapTupleIsValid(tuple
))
7544 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7545 errmsg("column \"%s\" of relation \"%s\" does not exist",
7546 colName
, RelationGetRelationName(rel
))));
7548 attnum
= ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnum
;
7550 /* Prevent them from altering a system attribute */
7553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7554 errmsg("cannot alter system column \"%s\"",
7558 * Okay, actually perform the catalog change ... if needed
7560 if (!((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
)
7562 ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
= true;
7564 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7567 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7568 * are set NOT NULL; however, if we can find a constraint which proves
7569 * this then we can skip that. We needn't bother looking if we've
7570 * already found that we must verify some other NOT NULL constraint.
7572 if (!tab
->verify_new_notnull
&&
7573 !NotNullImpliedByRelConstraints(rel
, (Form_pg_attribute
) GETSTRUCT(tuple
)))
7575 /* Tell Phase 3 it needs to test the constraint */
7576 tab
->verify_new_notnull
= true;
7579 ObjectAddressSubSet(address
, RelationRelationId
,
7580 RelationGetRelid(rel
), attnum
);
7583 address
= InvalidObjectAddress
;
7585 InvokeObjectPostAlterHook(RelationRelationId
,
7586 RelationGetRelid(rel
), attnum
);
7588 table_close(attr_rel
, RowExclusiveLock
);
7594 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7596 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7597 * commands against the partitions of a partitioned table if the user
7598 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7599 * or tries to create a primary key on it (which internally creates
7600 * AT_SetNotNull on the partitioned table). Such a command doesn't
7601 * allow us to actually modify any partition, but we want to let it
7602 * go through if the partitions are already properly marked.
7604 * In future, this might need to adjust the child table's state, likely
7605 * by incrementing an inheritance count for the attnotnull constraint.
7606 * For now we need only check for the presence of the flag.
7609 ATExecCheckNotNull(AlteredTableInfo
*tab
, Relation rel
,
7610 const char *colName
, LOCKMODE lockmode
)
7614 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
7616 if (!HeapTupleIsValid(tuple
))
7618 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7619 errmsg("column \"%s\" of relation \"%s\" does not exist",
7620 colName
, RelationGetRelationName(rel
))));
7622 if (!((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
)
7624 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7625 errmsg("constraint must be added to child tables too"),
7626 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7627 colName
, RelationGetRelationName(rel
)),
7628 errhint("Do not specify the ONLY keyword.")));
7630 ReleaseSysCache(tuple
);
7634 * NotNullImpliedByRelConstraints
7635 * Does rel's existing constraints imply NOT NULL for the given attribute?
7638 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
7640 NullTest
*nnulltest
= makeNode(NullTest
);
7642 nnulltest
->arg
= (Expr
*) makeVar(1,
7648 nnulltest
->nulltesttype
= IS_NOT_NULL
;
7651 * argisrow = false is correct even for a composite column, because
7652 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7653 * case, just IS DISTINCT FROM NULL.
7655 nnulltest
->argisrow
= false;
7656 nnulltest
->location
= -1;
7658 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
7661 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7662 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
7670 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7672 * Return the address of the affected column.
7674 static ObjectAddress
7675 ATExecColumnDefault(Relation rel
, const char *colName
,
7676 Node
*newDefault
, LOCKMODE lockmode
)
7678 TupleDesc tupdesc
= RelationGetDescr(rel
);
7680 ObjectAddress address
;
7683 * get the number of the attribute
7685 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7686 if (attnum
== InvalidAttrNumber
)
7688 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7689 errmsg("column \"%s\" of relation \"%s\" does not exist",
7690 colName
, RelationGetRelationName(rel
))));
7692 /* Prevent them from altering a system attribute */
7695 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7696 errmsg("cannot alter system column \"%s\"",
7699 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
7701 (errcode(ERRCODE_SYNTAX_ERROR
),
7702 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7703 colName
, RelationGetRelationName(rel
)),
7704 newDefault
? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
7706 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
7708 (errcode(ERRCODE_SYNTAX_ERROR
),
7709 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7710 colName
, RelationGetRelationName(rel
)),
7711 newDefault
|| TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
!= ATTRIBUTE_GENERATED_STORED
? 0 :
7712 errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
7715 * Remove any old default for the column. We use RESTRICT here for
7716 * safety, but at present we do not expect anything to depend on the
7719 * We treat removing the existing default as an internal operation when it
7720 * is preparatory to adding a new default, but as a user-initiated
7721 * operation when the user asked for a drop.
7723 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
7724 newDefault
!= NULL
);
7729 RawColumnDefault
*rawEnt
;
7731 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7732 rawEnt
->attnum
= attnum
;
7733 rawEnt
->raw_default
= newDefault
;
7734 rawEnt
->missingMode
= false;
7735 rawEnt
->generated
= '\0';
7738 * This function is intended for CREATE TABLE, so it processes a
7739 * _list_ of defaults, but we just do one.
7741 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7742 false, true, false, NULL
);
7745 ObjectAddressSubSet(address
, RelationRelationId
,
7746 RelationGetRelid(rel
), attnum
);
7751 * Add a pre-cooked default expression.
7753 * Return the address of the affected column.
7755 static ObjectAddress
7756 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
7759 ObjectAddress address
;
7761 /* We assume no checking is required */
7764 * Remove any old default for the column. We use RESTRICT here for
7765 * safety, but at present we do not expect anything to depend on the
7766 * default. (In ordinary cases, there could not be a default in place
7767 * anyway, but it's possible when combining LIKE with inheritance.)
7769 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
7772 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
7774 ObjectAddressSubSet(address
, RelationRelationId
,
7775 RelationGetRelid(rel
), attnum
);
7780 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7782 * Return the address of the affected column.
7784 static ObjectAddress
7785 ATExecAddIdentity(Relation rel
, const char *colName
,
7786 Node
*def
, LOCKMODE lockmode
)
7788 Relation attrelation
;
7790 Form_pg_attribute attTup
;
7792 ObjectAddress address
;
7793 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
7795 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
7797 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7798 if (!HeapTupleIsValid(tuple
))
7800 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7801 errmsg("column \"%s\" of relation \"%s\" does not exist",
7802 colName
, RelationGetRelationName(rel
))));
7803 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7804 attnum
= attTup
->attnum
;
7806 /* Can't alter a system attribute */
7809 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7810 errmsg("cannot alter system column \"%s\"",
7814 * Creating a column as identity implies NOT NULL, so adding the identity
7815 * to an existing column that is not NOT NULL would create a state that
7816 * cannot be reproduced without contortions.
7818 if (!attTup
->attnotnull
)
7820 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
7821 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
7822 colName
, RelationGetRelationName(rel
))));
7824 if (attTup
->attidentity
)
7826 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
7827 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
7828 colName
, RelationGetRelationName(rel
))));
7830 if (attTup
->atthasdef
)
7832 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
7833 errmsg("column \"%s\" of relation \"%s\" already has a default value",
7834 colName
, RelationGetRelationName(rel
))));
7836 attTup
->attidentity
= cdef
->identity
;
7837 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
7839 InvokeObjectPostAlterHook(RelationRelationId
,
7840 RelationGetRelid(rel
),
7842 ObjectAddressSubSet(address
, RelationRelationId
,
7843 RelationGetRelid(rel
), attnum
);
7844 heap_freetuple(tuple
);
7846 table_close(attrelation
, RowExclusiveLock
);
7852 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
7854 * Return the address of the affected column.
7856 static ObjectAddress
7857 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
, LOCKMODE lockmode
)
7860 DefElem
*generatedEl
= NULL
;
7862 Form_pg_attribute attTup
;
7864 Relation attrelation
;
7865 ObjectAddress address
;
7867 foreach(option
, castNode(List
, def
))
7869 DefElem
*defel
= lfirst_node(DefElem
, option
);
7871 if (strcmp(defel
->defname
, "generated") == 0)
7875 (errcode(ERRCODE_SYNTAX_ERROR
),
7876 errmsg("conflicting or redundant options")));
7877 generatedEl
= defel
;
7880 elog(ERROR
, "option \"%s\" not recognized",
7885 * Even if there is nothing to change here, we run all the checks. There
7886 * will be a subsequent ALTER SEQUENCE that relies on everything being
7890 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
7891 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7892 if (!HeapTupleIsValid(tuple
))
7894 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7895 errmsg("column \"%s\" of relation \"%s\" does not exist",
7896 colName
, RelationGetRelationName(rel
))));
7898 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7899 attnum
= attTup
->attnum
;
7903 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7904 errmsg("cannot alter system column \"%s\"",
7907 if (!attTup
->attidentity
)
7909 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
7910 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7911 colName
, RelationGetRelationName(rel
))));
7915 attTup
->attidentity
= defGetInt32(generatedEl
);
7916 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
7918 InvokeObjectPostAlterHook(RelationRelationId
,
7919 RelationGetRelid(rel
),
7921 ObjectAddressSubSet(address
, RelationRelationId
,
7922 RelationGetRelid(rel
), attnum
);
7925 address
= InvalidObjectAddress
;
7927 heap_freetuple(tuple
);
7928 table_close(attrelation
, RowExclusiveLock
);
7934 * ALTER TABLE ALTER COLUMN DROP IDENTITY
7936 * Return the address of the affected column.
7938 static ObjectAddress
7939 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
7942 Form_pg_attribute attTup
;
7944 Relation attrelation
;
7945 ObjectAddress address
;
7947 ObjectAddress seqaddress
;
7949 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
7950 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7951 if (!HeapTupleIsValid(tuple
))
7953 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7954 errmsg("column \"%s\" of relation \"%s\" does not exist",
7955 colName
, RelationGetRelationName(rel
))));
7957 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7958 attnum
= attTup
->attnum
;
7962 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7963 errmsg("cannot alter system column \"%s\"",
7966 if (!attTup
->attidentity
)
7970 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
7971 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7972 colName
, RelationGetRelationName(rel
))));
7976 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
7977 colName
, RelationGetRelationName(rel
))));
7978 heap_freetuple(tuple
);
7979 table_close(attrelation
, RowExclusiveLock
);
7980 return InvalidObjectAddress
;
7984 attTup
->attidentity
= '\0';
7985 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
7987 InvokeObjectPostAlterHook(RelationRelationId
,
7988 RelationGetRelid(rel
),
7990 ObjectAddressSubSet(address
, RelationRelationId
,
7991 RelationGetRelid(rel
), attnum
);
7992 heap_freetuple(tuple
);
7994 table_close(attrelation
, RowExclusiveLock
);
7996 /* drop the internal sequence */
7997 seqid
= getIdentitySequence(RelationGetRelid(rel
), attnum
, false);
7998 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
7999 RelationRelationId
, DEPENDENCY_INTERNAL
);
8000 CommandCounterIncrement();
8001 seqaddress
.classId
= RelationRelationId
;
8002 seqaddress
.objectId
= seqid
;
8003 seqaddress
.objectSubId
= 0;
8004 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8010 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8013 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8016 * Reject ONLY if there are child tables. We could implement this, but it
8017 * is a bit complicated. GENERATED clauses must be attached to the column
8018 * definition and cannot be added later like DEFAULT, so if a child table
8019 * has a generation expression that the parent does not have, the child
8020 * column will necessarily be an attislocal column. So to implement ONLY
8021 * here, we'd need extra code to update attislocal of the direct child
8022 * tables, somewhat similar to how DROP COLUMN does it, so that the
8023 * resulting state can be properly dumped and restored.
8026 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8029 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8032 * Cannot drop generation expression from inherited columns.
8037 Form_pg_attribute attTup
;
8039 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8040 if (!HeapTupleIsValid(tuple
))
8042 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8043 errmsg("column \"%s\" of relation \"%s\" does not exist",
8044 cmd
->name
, RelationGetRelationName(rel
))));
8046 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8048 if (attTup
->attinhcount
> 0)
8050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8051 errmsg("cannot drop generation expression from inherited column")));
8056 * Return the address of the affected column.
8058 static ObjectAddress
8059 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8062 Form_pg_attribute attTup
;
8064 Relation attrelation
;
8066 ObjectAddress address
;
8068 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8069 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8070 if (!HeapTupleIsValid(tuple
))
8072 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8073 errmsg("column \"%s\" of relation \"%s\" does not exist",
8074 colName
, RelationGetRelationName(rel
))));
8076 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8077 attnum
= attTup
->attnum
;
8081 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8082 errmsg("cannot alter system column \"%s\"",
8085 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8089 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8090 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8091 colName
, RelationGetRelationName(rel
))));
8095 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8096 colName
, RelationGetRelationName(rel
))));
8097 heap_freetuple(tuple
);
8098 table_close(attrelation
, RowExclusiveLock
);
8099 return InvalidObjectAddress
;
8104 * Mark the column as no longer generated. (The atthasdef flag needs to
8105 * get cleared too, but RemoveAttrDefault will handle that.)
8107 attTup
->attgenerated
= '\0';
8108 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8110 InvokeObjectPostAlterHook(RelationRelationId
,
8111 RelationGetRelid(rel
),
8113 heap_freetuple(tuple
);
8115 table_close(attrelation
, RowExclusiveLock
);
8118 * Drop the dependency records of the GENERATED expression, in particular
8119 * its INTERNAL dependency on the column, which would otherwise cause
8120 * dependency.c to refuse to perform the deletion.
8122 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8123 if (!OidIsValid(attrdefoid
))
8124 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8125 RelationGetRelid(rel
), attnum
);
8126 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8128 /* Make above changes visible */
8129 CommandCounterIncrement();
8132 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8133 * safety, but at present we do not expect anything to depend on the
8136 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8139 ObjectAddressSubSet(address
, RelationRelationId
,
8140 RelationGetRelid(rel
), attnum
);
8145 * ALTER TABLE ALTER COLUMN SET STATISTICS
8147 * Return value is the address of the modified column
8149 static ObjectAddress
8150 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8153 Relation attrelation
;
8155 Form_pg_attribute attrtuple
;
8157 ObjectAddress address
;
8160 * We allow referencing columns by numbers only for indexes, since table
8161 * column numbers could contain gaps if columns are later dropped.
8163 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8164 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8167 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8168 errmsg("cannot refer to non-index column by number")));
8170 Assert(IsA(newValue
, Integer
));
8171 newtarget
= intVal(newValue
);
8174 * Limit target to a sane range
8179 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8180 errmsg("statistics target %d is too low",
8183 else if (newtarget
> MAX_STATISTICS_TARGET
)
8185 newtarget
= MAX_STATISTICS_TARGET
;
8187 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8188 errmsg("lowering statistics target to %d",
8192 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8196 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8198 if (!HeapTupleIsValid(tuple
))
8200 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8201 errmsg("column \"%s\" of relation \"%s\" does not exist",
8202 colName
, RelationGetRelationName(rel
))));
8206 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), colNum
);
8208 if (!HeapTupleIsValid(tuple
))
8210 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8211 errmsg("column number %d of relation \"%s\" does not exist",
8212 colNum
, RelationGetRelationName(rel
))));
8215 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8217 attnum
= attrtuple
->attnum
;
8220 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8221 errmsg("cannot alter system column \"%s\"",
8224 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8225 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8227 if (attnum
> rel
->rd_index
->indnkeyatts
)
8229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8230 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8231 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8232 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8234 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8235 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8236 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8237 errhint("Alter statistics on table column instead.")));
8240 attrtuple
->attstattarget
= newtarget
;
8242 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8244 InvokeObjectPostAlterHook(RelationRelationId
,
8245 RelationGetRelid(rel
),
8247 ObjectAddressSubSet(address
, RelationRelationId
,
8248 RelationGetRelid(rel
), attnum
);
8249 heap_freetuple(tuple
);
8251 table_close(attrelation
, RowExclusiveLock
);
8257 * Return value is the address of the modified column
8259 static ObjectAddress
8260 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8261 bool isReset
, LOCKMODE lockmode
)
8263 Relation attrelation
;
8266 Form_pg_attribute attrtuple
;
8271 ObjectAddress address
;
8272 Datum repl_val
[Natts_pg_attribute
];
8273 bool repl_null
[Natts_pg_attribute
];
8274 bool repl_repl
[Natts_pg_attribute
];
8276 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8278 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8280 if (!HeapTupleIsValid(tuple
))
8282 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8283 errmsg("column \"%s\" of relation \"%s\" does not exist",
8284 colName
, RelationGetRelationName(rel
))));
8285 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8287 attnum
= attrtuple
->attnum
;
8290 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8291 errmsg("cannot alter system column \"%s\"",
8294 /* Generate new proposed attoptions (text array) */
8295 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8297 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8298 castNode(List
, options
), NULL
, NULL
,
8300 /* Validate new options */
8301 (void) attribute_reloptions(newOptions
, true);
8303 /* Build new tuple. */
8304 memset(repl_null
, false, sizeof(repl_null
));
8305 memset(repl_repl
, false, sizeof(repl_repl
));
8306 if (newOptions
!= (Datum
) 0)
8307 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8309 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8310 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8311 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8312 repl_val
, repl_null
, repl_repl
);
8314 /* Update system catalog. */
8315 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8317 InvokeObjectPostAlterHook(RelationRelationId
,
8318 RelationGetRelid(rel
),
8320 ObjectAddressSubSet(address
, RelationRelationId
,
8321 RelationGetRelid(rel
), attnum
);
8323 heap_freetuple(newtuple
);
8325 ReleaseSysCache(tuple
);
8327 table_close(attrelation
, RowExclusiveLock
);
8333 * Helper function for ATExecSetStorage and ATExecSetCompression
8335 * Set the attstorage and/or attcompression fields for index columns
8336 * associated with the specified table column.
8339 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
8341 bool setstorage
, char newstorage
,
8342 bool setcompression
, char newcompression
,
8347 foreach(lc
, RelationGetIndexList(rel
))
8349 Oid indexoid
= lfirst_oid(lc
);
8351 AttrNumber indattnum
= 0;
8354 indrel
= index_open(indexoid
, lockmode
);
8356 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
8358 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
8367 index_close(indrel
, lockmode
);
8371 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
8373 if (HeapTupleIsValid(tuple
))
8375 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8378 attrtuple
->attstorage
= newstorage
;
8381 attrtuple
->attcompression
= newcompression
;
8383 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8385 InvokeObjectPostAlterHook(RelationRelationId
,
8386 RelationGetRelid(rel
),
8389 heap_freetuple(tuple
);
8392 index_close(indrel
, lockmode
);
8397 * ALTER TABLE ALTER COLUMN SET STORAGE
8399 * Return value is the address of the modified column
8401 static ObjectAddress
8402 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
8404 Relation attrelation
;
8406 Form_pg_attribute attrtuple
;
8408 ObjectAddress address
;
8410 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8412 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8414 if (!HeapTupleIsValid(tuple
))
8416 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8417 errmsg("column \"%s\" of relation \"%s\" does not exist",
8418 colName
, RelationGetRelationName(rel
))));
8419 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8421 attnum
= attrtuple
->attnum
;
8424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8425 errmsg("cannot alter system column \"%s\"",
8428 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
8430 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8432 InvokeObjectPostAlterHook(RelationRelationId
,
8433 RelationGetRelid(rel
),
8437 * Apply the change to indexes as well (only for simple index columns,
8438 * matching behavior of index.c ConstructTupleDescriptor()).
8440 SetIndexStorageProperties(rel
, attrelation
, attnum
,
8441 true, attrtuple
->attstorage
,
8445 heap_freetuple(tuple
);
8447 table_close(attrelation
, RowExclusiveLock
);
8449 ObjectAddressSubSet(address
, RelationRelationId
,
8450 RelationGetRelid(rel
), attnum
);
8456 * ALTER TABLE DROP COLUMN
8458 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8459 * because we have to decide at runtime whether to recurse or not depending
8460 * on whether attinhcount goes to zero or not. (We can't check this in a
8461 * static pre-pass because it won't handle multiple inheritance situations
8465 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
8466 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
8467 AlterTableUtilityContext
*context
)
8469 if (rel
->rd_rel
->reloftype
&& !recursing
)
8471 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
8472 errmsg("cannot drop column from typed table")));
8474 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
8475 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
8478 cmd
->recurse
= true;
8482 * Drops column 'colName' from relation 'rel' and returns the address of the
8483 * dropped column. The column is also dropped (or marked as no longer
8484 * inherited from relation) from the relation's inheritance children, if any.
8486 * In the recursive invocations for inheritance child relations, instead of
8487 * dropping the column directly (if to be dropped at all), its object address
8488 * is added to 'addrs', which must be non-NULL in such invocations. All
8489 * columns are dropped at the same time after all the children have been
8490 * checked recursively.
8492 static ObjectAddress
8493 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
8494 DropBehavior behavior
,
8495 bool recurse
, bool recursing
,
8496 bool missing_ok
, LOCKMODE lockmode
,
8497 ObjectAddresses
*addrs
)
8500 Form_pg_attribute targetatt
;
8503 ObjectAddress object
;
8506 /* At top level, permission check was done in ATPrepCmd, else do it */
8508 ATSimplePermissions(AT_DropColumn
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
8510 /* Initialize addrs on the first invocation */
8511 Assert(!recursing
|| addrs
!= NULL
);
8513 addrs
= new_object_addresses();
8516 * get the number of the attribute
8518 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8519 if (!HeapTupleIsValid(tuple
))
8524 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8525 errmsg("column \"%s\" of relation \"%s\" does not exist",
8526 colName
, RelationGetRelationName(rel
))));
8531 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8532 colName
, RelationGetRelationName(rel
))));
8533 return InvalidObjectAddress
;
8536 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8538 attnum
= targetatt
->attnum
;
8540 /* Can't drop a system attribute */
8543 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8544 errmsg("cannot drop system column \"%s\"",
8548 * Don't drop inherited columns, unless recursing (presumably from a drop
8549 * of the parent column)
8551 if (targetatt
->attinhcount
> 0 && !recursing
)
8553 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8554 errmsg("cannot drop inherited column \"%s\"",
8558 * Don't drop columns used in the partition key, either. (If we let this
8559 * go through, the key column's dependencies would cause a cascaded drop
8560 * of the whole table, which is surely not what the user expected.)
8562 if (has_partition_attrs(rel
,
8563 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
8566 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8567 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8568 colName
, RelationGetRelationName(rel
))));
8570 ReleaseSysCache(tuple
);
8573 * Propagate to children as appropriate. Unlike most other ALTER
8574 * routines, we have to do this one level of recursion at a time; we can't
8575 * use find_all_inheritors to do it in one pass.
8578 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8586 * In case of a partitioned table, the column must be dropped from the
8587 * partitions as well.
8589 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
8591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8592 errmsg("cannot drop column from only the partitioned table when partitions exist"),
8593 errhint("Do not specify the ONLY keyword.")));
8595 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
8596 foreach(child
, children
)
8598 Oid childrelid
= lfirst_oid(child
);
8600 Form_pg_attribute childatt
;
8602 /* find_inheritance_children already got lock */
8603 childrel
= table_open(childrelid
, NoLock
);
8604 CheckTableNotInUse(childrel
, "ALTER TABLE");
8606 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
8607 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
8608 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
8609 colName
, childrelid
);
8610 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8612 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
8613 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
8614 childrelid
, colName
);
8619 * If the child column has other definition sources, just
8620 * decrement its inheritance count; if not, recurse to delete
8623 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
8625 /* Time to delete this child column, too */
8626 ATExecDropColumn(wqueue
, childrel
, colName
,
8627 behavior
, true, true,
8628 false, lockmode
, addrs
);
8632 /* Child column must survive my deletion */
8633 childatt
->attinhcount
--;
8635 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
8637 /* Make update visible */
8638 CommandCounterIncrement();
8644 * If we were told to drop ONLY in this table (no recursion),
8645 * we need to mark the inheritors' attributes as locally
8646 * defined rather than inherited.
8648 childatt
->attinhcount
--;
8649 childatt
->attislocal
= true;
8651 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
8653 /* Make update visible */
8654 CommandCounterIncrement();
8657 heap_freetuple(tuple
);
8659 table_close(childrel
, NoLock
);
8661 table_close(attr_rel
, RowExclusiveLock
);
8664 /* Add object to delete */
8665 object
.classId
= RelationRelationId
;
8666 object
.objectId
= RelationGetRelid(rel
);
8667 object
.objectSubId
= attnum
;
8668 add_exact_object_address(&object
, addrs
);
8672 /* Recursion has ended, drop everything that was collected */
8673 performMultipleDeletions(addrs
, behavior
, 0);
8674 free_object_addresses(addrs
);
8681 * ALTER TABLE ADD INDEX
8683 * There is no such command in the grammar, but parse_utilcmd.c converts
8684 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
8685 * us schedule creation of the index at the appropriate time during ALTER.
8687 * Return value is the address of the new index.
8689 static ObjectAddress
8690 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
8691 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
8696 ObjectAddress address
;
8698 Assert(IsA(stmt
, IndexStmt
));
8699 Assert(!stmt
->concurrent
);
8701 /* The IndexStmt has already been through transformIndexStmt */
8702 Assert(stmt
->transformed
);
8704 /* suppress schema rights check when rebuilding existing index */
8705 check_rights
= !is_rebuild
;
8706 /* skip index build if phase 3 will do it or we're reusing an old one */
8707 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
8708 /* suppress notices when rebuilding existing index */
8711 address
= DefineIndex(RelationGetRelid(rel
),
8713 InvalidOid
, /* no predefined OID */
8714 InvalidOid
, /* no parent index */
8715 InvalidOid
, /* no parent constraint */
8716 -1, /* total_parts unknown */
8717 true, /* is_alter_table */
8719 false, /* check_not_in_use - we did it already */
8724 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
8725 * new index instead of building from scratch. Restore associated fields.
8726 * This may store InvalidSubTransactionId in both fields, in which case
8727 * relcache.c will assume it can rebuild the relcache entry. Hence, do
8728 * this after the CCI that made catalog rows visible to any rebuild. The
8729 * DROP of the old edition of this index will have scheduled the storage
8730 * for deletion at commit, so cancel that pending deletion.
8732 if (RelFileNumberIsValid(stmt
->oldNumber
))
8734 Relation irel
= index_open(address
.objectId
, NoLock
);
8736 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
8737 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
8738 RelationPreserveStorage(irel
->rd_locator
, true);
8739 index_close(irel
, NoLock
);
8746 * ALTER TABLE ADD STATISTICS
8748 * This is no such command in the grammar, but we use this internally to add
8749 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
8750 * column type change.
8752 static ObjectAddress
8753 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
8754 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
8756 ObjectAddress address
;
8758 Assert(IsA(stmt
, CreateStatsStmt
));
8760 /* The CreateStatsStmt has already been through transformStatsStmt */
8761 Assert(stmt
->transformed
);
8763 address
= CreateStatistics(stmt
);
8769 * ALTER TABLE ADD CONSTRAINT USING INDEX
8771 * Returns the address of the new constraint.
8773 static ObjectAddress
8774 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
8775 IndexStmt
*stmt
, LOCKMODE lockmode
)
8777 Oid index_oid
= stmt
->indexOid
;
8780 IndexInfo
*indexInfo
;
8781 char *constraintName
;
8782 char constraintType
;
8783 ObjectAddress address
;
8786 Assert(IsA(stmt
, IndexStmt
));
8787 Assert(OidIsValid(index_oid
));
8788 Assert(stmt
->isconstraint
);
8791 * Doing this on partitioned tables is not a simple feature to implement,
8792 * so let's punt for now.
8794 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
8796 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8797 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
8799 indexRel
= index_open(index_oid
, AccessShareLock
);
8801 indexName
= pstrdup(RelationGetRelationName(indexRel
));
8803 indexInfo
= BuildIndexInfo(indexRel
);
8805 /* this should have been checked at parse time */
8806 if (!indexInfo
->ii_Unique
)
8807 elog(ERROR
, "index \"%s\" is not unique", indexName
);
8810 * Determine name to assign to constraint. We require a constraint to
8811 * have the same name as the underlying index; therefore, use the index's
8812 * existing name as the default constraint name, and if the user
8813 * explicitly gives some other name for the constraint, rename the index
8816 constraintName
= stmt
->idxname
;
8817 if (constraintName
== NULL
)
8818 constraintName
= indexName
;
8819 else if (strcmp(constraintName
, indexName
) != 0)
8822 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
8823 indexName
, constraintName
)));
8824 RenameRelationInternal(index_oid
, constraintName
, false, true);
8827 /* Extra checks needed if making primary key */
8829 index_check_primary_key(rel
, indexInfo
, true, stmt
);
8831 /* Note we currently don't support EXCLUSION constraints here */
8833 constraintType
= CONSTRAINT_PRIMARY
;
8835 constraintType
= CONSTRAINT_UNIQUE
;
8837 /* Create the catalog entries for the constraint */
8838 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
8839 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
8840 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
8841 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
8842 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
8844 address
= index_constraint_create(rel
,
8851 allowSystemTableMods
,
8852 false); /* is_internal */
8854 index_close(indexRel
, NoLock
);
8860 * ALTER TABLE ADD CONSTRAINT
8862 * Return value is the address of the new constraint; if no constraint was
8863 * added, InvalidObjectAddress is returned.
8865 static ObjectAddress
8866 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
8867 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
8870 ObjectAddress address
= InvalidObjectAddress
;
8872 Assert(IsA(newConstraint
, Constraint
));
8875 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
8876 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
8877 * switch anyway to make it easier to add more code later.
8879 switch (newConstraint
->contype
)
8883 ATAddCheckConstraint(wqueue
, tab
, rel
,
8884 newConstraint
, recurse
, false, is_readd
,
8888 case CONSTR_FOREIGN
:
8891 * Assign or validate constraint name
8893 if (newConstraint
->conname
)
8895 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
8896 RelationGetRelid(rel
),
8897 newConstraint
->conname
))
8899 (errcode(ERRCODE_DUPLICATE_OBJECT
),
8900 errmsg("constraint \"%s\" for relation \"%s\" already exists",
8901 newConstraint
->conname
,
8902 RelationGetRelationName(rel
))));
8905 newConstraint
->conname
=
8906 ChooseConstraintName(RelationGetRelationName(rel
),
8907 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
8909 RelationGetNamespace(rel
),
8912 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
8919 elog(ERROR
, "unrecognized constraint type: %d",
8920 (int) newConstraint
->contype
);
8927 * Generate the column-name portion of the constraint name for a new foreign
8928 * key given the list of column names that reference the referenced
8929 * table. This will be passed to ChooseConstraintName along with the parent
8930 * table name and the "fkey" suffix.
8932 * We know that less than NAMEDATALEN characters will actually be used, so we
8933 * can truncate the result once we've generated that many.
8935 * XXX see also ChooseExtendedStatisticNameAddition and
8936 * ChooseIndexNameAddition.
8939 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
8941 char buf
[NAMEDATALEN
* 2];
8946 foreach(lc
, colnames
)
8948 const char *name
= strVal(lfirst(lc
));
8951 buf
[buflen
++] = '_'; /* insert _ between names */
8954 * At this point we have buflen <= NAMEDATALEN. name should be less
8955 * than NAMEDATALEN already, but use strlcpy for paranoia.
8957 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
8958 buflen
+= strlen(buf
+ buflen
);
8959 if (buflen
>= NAMEDATALEN
)
8962 return pstrdup(buf
);
8966 * Add a check constraint to a single table and its children. Returns the
8967 * address of the constraint added to the parent relation, if one gets added,
8968 * or InvalidObjectAddress otherwise.
8970 * Subroutine for ATExecAddConstraint.
8972 * We must recurse to child tables during execution, rather than using
8973 * ALTER TABLE's normal prep-time recursion. The reason is that all the
8974 * constraints *must* be given the same name, else they won't be seen as
8975 * related later. If the user didn't explicitly specify a name, then
8976 * AddRelationNewConstraints would normally assign different names to the
8977 * child constraints. To fix that, we must capture the name assigned at
8978 * the parent table and pass that down.
8980 static ObjectAddress
8981 ATAddCheckConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
8982 Constraint
*constr
, bool recurse
, bool recursing
,
8983 bool is_readd
, LOCKMODE lockmode
)
8989 ObjectAddress address
= InvalidObjectAddress
;
8991 /* At top level, permission check was done in ATPrepCmd, else do it */
8993 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
8996 * Call AddRelationNewConstraints to do the work, making sure it works on
8997 * a copy of the Constraint so transformExpr can't modify the original. It
8998 * returns a list of cooked constraints.
9000 * If the constraint ends up getting merged with a pre-existing one, it's
9001 * omitted from the returned list, which is what we want: we do not need
9002 * to do any validation work. That can only happen at child tables,
9003 * though, since we disallow merging at the top level.
9005 newcons
= AddRelationNewConstraints(rel
, NIL
,
9006 list_make1(copyObject(constr
)),
9007 recursing
|| is_readd
, /* allow_merge */
9008 !recursing
, /* is_local */
9009 is_readd
, /* is_internal */
9010 NULL
); /* queryString not available
9013 /* we don't expect more than one constraint here */
9014 Assert(list_length(newcons
) <= 1);
9016 /* Add each to-be-validated constraint to Phase 3's queue */
9017 foreach(lcon
, newcons
)
9019 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9021 if (!ccon
->skip_validation
)
9023 NewConstraint
*newcon
;
9025 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9026 newcon
->name
= ccon
->name
;
9027 newcon
->contype
= ccon
->contype
;
9028 newcon
->qual
= ccon
->expr
;
9030 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9033 /* Save the actually assigned name if it was defaulted */
9034 if (constr
->conname
== NULL
)
9035 constr
->conname
= ccon
->name
;
9037 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9040 /* At this point we must have a locked-down name to use */
9041 Assert(constr
->conname
!= NULL
);
9043 /* Advance command counter in case same table is visited multiple times */
9044 CommandCounterIncrement();
9047 * If the constraint got merged with an existing constraint, we're done.
9048 * We mustn't recurse to child tables in this case, because they've
9049 * already got the constraint, and visiting them again would lead to an
9050 * incorrect value for coninhcount.
9056 * If adding a NO INHERIT constraint, no need to find our children.
9058 if (constr
->is_no_inherit
)
9062 * Propagate to children as appropriate. Unlike most other ALTER
9063 * routines, we have to do this one level of recursion at a time; we can't
9064 * use find_all_inheritors to do it in one pass.
9067 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9070 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9071 * constraint creation only if there are no children currently. Error out
9074 if (!recurse
&& children
!= NIL
)
9076 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9077 errmsg("constraint must be added to child tables too")));
9079 foreach(child
, children
)
9081 Oid childrelid
= lfirst_oid(child
);
9083 AlteredTableInfo
*childtab
;
9085 /* find_inheritance_children already got lock */
9086 childrel
= table_open(childrelid
, NoLock
);
9087 CheckTableNotInUse(childrel
, "ALTER TABLE");
9089 /* Find or create work queue entry for this table */
9090 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9092 /* Recurse to child */
9093 ATAddCheckConstraint(wqueue
, childtab
, childrel
,
9094 constr
, recurse
, true, is_readd
, lockmode
);
9096 table_close(childrel
, NoLock
);
9103 * Add a foreign-key constraint to a single table; return the new constraint's
9106 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9107 * lock on the rel, and have done appropriate validity checks for it.
9108 * We do permissions checks here, however.
9110 * When the referenced or referencing tables (or both) are partitioned,
9111 * multiple pg_constraint rows are required -- one for each partitioned table
9112 * and each partition on each side (fortunately, not one for every combination
9113 * thereof). We also need action triggers on each leaf partition on the
9114 * referenced side, and check triggers on each leaf partition on the
9117 static ObjectAddress
9118 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9119 Constraint
*fkconstraint
,
9120 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9123 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9124 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9125 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9126 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9127 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9128 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9129 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9130 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9131 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9138 ObjectAddress address
;
9139 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9142 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9143 * delete rows out from under us.
9145 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9146 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9148 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9151 * Validity checks (permission checks wait till we have the column
9154 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9158 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9159 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9160 RelationGetRelationName(rel
),
9161 RelationGetRelationName(pkrel
))));
9162 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9164 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9165 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9166 RelationGetRelationName(rel
),
9167 RelationGetRelationName(pkrel
)),
9168 errdetail("This feature is not yet supported on partitioned tables.")));
9171 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9172 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9174 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9175 errmsg("referenced relation \"%s\" is not a table",
9176 RelationGetRelationName(pkrel
))));
9178 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9180 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9181 errmsg("permission denied: \"%s\" is a system catalog",
9182 RelationGetRelationName(pkrel
))));
9185 * References from permanent or unlogged tables to temp tables, and from
9186 * permanent tables to unlogged tables, are disallowed because the
9187 * referenced data can vanish out from under us. References from temp
9188 * tables to any other table type are also disallowed, because other
9189 * backends might need to run the RI triggers on the perm table, but they
9190 * can't reliably see tuples in the local buffers of other backends.
9192 switch (rel
->rd_rel
->relpersistence
)
9194 case RELPERSISTENCE_PERMANENT
:
9195 if (!RelationIsPermanent(pkrel
))
9197 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9198 errmsg("constraints on permanent tables may reference only permanent tables")));
9200 case RELPERSISTENCE_UNLOGGED
:
9201 if (!RelationIsPermanent(pkrel
)
9202 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9204 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9205 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9207 case RELPERSISTENCE_TEMP
:
9208 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9210 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9211 errmsg("constraints on temporary tables may reference only temporary tables")));
9212 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
9214 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9215 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9220 * Look up the referencing attributes to make sure they exist, and record
9221 * their attnums and type OIDs.
9223 numfks
= transformColumnNameList(RelationGetRelid(rel
),
9224 fkconstraint
->fk_attrs
,
9225 fkattnum
, fktypoid
);
9227 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
9228 fkconstraint
->fk_del_set_cols
,
9229 fkdelsetcols
, NULL
);
9230 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
9231 numfkdelsetcols
, fkdelsetcols
,
9232 fkconstraint
->fk_del_set_cols
);
9235 * If the attribute list for the referenced table was omitted, lookup the
9236 * definition of the primary key and use it. Otherwise, validate the
9237 * supplied attribute list. In either case, discover the index OID and
9238 * index opclasses, and the attnums and type OIDs of the attributes.
9240 if (fkconstraint
->pk_attrs
== NIL
)
9242 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
9243 &fkconstraint
->pk_attrs
,
9249 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
9250 fkconstraint
->pk_attrs
,
9251 pkattnum
, pktypoid
);
9252 /* Look for an index matching the column list */
9253 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
9258 * Now we can check permissions.
9260 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
9263 * Check some things for generated columns.
9265 for (i
= 0; i
< numfks
; i
++)
9267 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
9272 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9274 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9275 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
9276 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
9278 (errcode(ERRCODE_SYNTAX_ERROR
),
9279 errmsg("invalid %s action for foreign key constraint containing generated column",
9281 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
9282 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
9284 (errcode(ERRCODE_SYNTAX_ERROR
),
9285 errmsg("invalid %s action for foreign key constraint containing generated column",
9291 * Look up the equality operators to use in the constraint.
9293 * Note that we have to be careful about the difference between the actual
9294 * PK column type and the opclass' declared input type, which might be
9295 * only binary-compatible with it. The declared opcintype is the right
9296 * thing to probe pg_amop with.
9298 if (numfks
!= numpks
)
9300 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9301 errmsg("number of referencing and referenced columns for foreign key disagree")));
9304 * On the strength of a previous constraint, we might avoid scanning
9305 * tables to validate this one. See below.
9307 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
9308 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
9310 for (i
= 0; i
< numpks
; i
++)
9312 Oid pktype
= pktypoid
[i
];
9313 Oid fktype
= fktypoid
[i
];
9316 Form_pg_opclass cla_tup
;
9326 /* We need several fields out of the pg_opclass entry */
9327 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
9328 if (!HeapTupleIsValid(cla_ht
))
9329 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
9330 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
9331 amid
= cla_tup
->opcmethod
;
9332 opfamily
= cla_tup
->opcfamily
;
9333 opcintype
= cla_tup
->opcintype
;
9334 ReleaseSysCache(cla_ht
);
9337 * Check it's a btree; currently this can never fail since no other
9338 * index AMs support unique indexes. If we ever did have other types
9339 * of unique indexes, we'd need a way to determine which operator
9340 * strategy number is equality. (Is it reasonable to insist that
9341 * every such index AM use btree's number for equality?)
9343 if (amid
!= BTREE_AM_OID
)
9344 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
9345 eqstrategy
= BTEqualStrategyNumber
;
9348 * There had better be a primary equality operator for the index.
9349 * We'll use it for PK = PK comparisons.
9351 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
9354 if (!OidIsValid(ppeqop
))
9355 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
9356 eqstrategy
, opcintype
, opcintype
, opfamily
);
9359 * Are there equality operators that take exactly the FK type? Assume
9360 * we should look through any domain here.
9362 fktyped
= getBaseType(fktype
);
9364 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
9366 if (OidIsValid(pfeqop
))
9368 pfeqop_right
= fktyped
;
9369 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
9374 /* keep compiler quiet */
9375 pfeqop_right
= InvalidOid
;
9376 ffeqop
= InvalidOid
;
9379 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9382 * Otherwise, look for an implicit cast from the FK type to the
9383 * opcintype, and if found, use the primary equality operator.
9384 * This is a bit tricky because opcintype might be a polymorphic
9385 * type such as ANYARRAY or ANYENUM; so what we have to test is
9386 * whether the two actual column types can be concurrently cast to
9387 * that type. (Otherwise, we'd fail to reject combinations such
9388 * as int[] and point[].)
9390 Oid input_typeids
[2];
9391 Oid target_typeids
[2];
9393 input_typeids
[0] = pktype
;
9394 input_typeids
[1] = fktype
;
9395 target_typeids
[0] = opcintype
;
9396 target_typeids
[1] = opcintype
;
9397 if (can_coerce_type(2, input_typeids
, target_typeids
,
9400 pfeqop
= ffeqop
= ppeqop
;
9401 pfeqop_right
= opcintype
;
9405 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9407 (errcode(ERRCODE_DATATYPE_MISMATCH
),
9408 errmsg("foreign key constraint \"%s\" cannot be implemented",
9409 fkconstraint
->conname
),
9410 errdetail("Key columns \"%s\" and \"%s\" "
9411 "are of incompatible types: %s and %s.",
9412 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
9413 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
9414 format_type_be(fktype
),
9415 format_type_be(pktype
))));
9420 * When a pfeqop changes, revalidate the constraint. We could
9421 * permit intra-opfamily changes, but that adds subtle complexity
9422 * without any concrete benefit for core types. We need not
9423 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9425 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
9426 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
9433 CoercionPathType old_pathtype
;
9434 CoercionPathType new_pathtype
;
9437 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
9441 * Identify coercion pathways from each of the old and new FK-side
9442 * column types to the right (foreign) operand type of the pfeqop.
9443 * We may assume that pg_constraint.conkey is not changing.
9445 old_fktype
= attr
->atttypid
;
9446 new_fktype
= fktype
;
9447 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
9449 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
9453 * Upon a change to the cast from the FK column to its pfeqop
9454 * operand, revalidate the constraint. For this evaluation, a
9455 * binary coercion cast is equivalent to no cast at all. While
9456 * type implementors should design implicit casts with an eye
9457 * toward consistency of operations like equality, we cannot
9458 * assume here that they have done so.
9460 * A function with a polymorphic argument could change behavior
9461 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9462 * when the cast destination is polymorphic, we only avoid
9463 * revalidation if the input type has not changed at all. Given
9464 * just the core data types and operator classes, this requirement
9465 * prevents no would-be optimizations.
9467 * If the cast converts from a base type to a domain thereon, then
9468 * that domain type must be the opcintype of the unique index.
9469 * Necessarily, the primary key column must then be of the domain
9470 * type. Since the constraint was previously valid, all values on
9471 * the foreign side necessarily exist on the primary side and in
9472 * turn conform to the domain. Consequently, we need not treat
9473 * domains specially here.
9475 * Since we require that all collations share the same notion of
9476 * equality (which they do, because texteq reduces to bitwise
9477 * equality), we don't compare collation here.
9479 * We need not directly consider the PK type. It's necessarily
9480 * binary coercible to the opcintype of the unique index column,
9481 * and ri_triggers.c will only deal with PK datums in terms of
9482 * that opcintype. Changing the opcintype also changes pfeqop.
9484 old_check_ok
= (new_pathtype
== old_pathtype
&&
9485 new_castfunc
== old_castfunc
&&
9486 (!IsPolymorphicType(pfeqop_right
) ||
9487 new_fktype
== old_fktype
));
9490 pfeqoperators
[i
] = pfeqop
;
9491 ppeqoperators
[i
] = ppeqop
;
9492 ffeqoperators
[i
] = ffeqop
;
9496 * Create all the constraint and trigger objects, recursing to partitions
9497 * as necessary. First handle the referenced side.
9499 address
= addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, pkrel
,
9501 InvalidOid
, /* no parent constraint */
9511 InvalidOid
, InvalidOid
);
9513 /* Now handle the referencing side. */
9514 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
9527 InvalidOid
, InvalidOid
);
9530 * Done. Close pk table, but keep lock until we've committed.
9532 table_close(pkrel
, NoLock
);
9538 * validateFkOnDeleteSetColumns
9539 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9540 * column lists are valid.
9543 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
9544 int numfksetcols
, const int16
*fksetcolsattnums
,
9547 for (int i
= 0; i
< numfksetcols
; i
++)
9549 int16 setcol_attnum
= fksetcolsattnums
[i
];
9552 for (int j
= 0; j
< numfks
; j
++)
9554 if (fkattnums
[j
] == setcol_attnum
)
9563 char *col
= strVal(list_nth(fksetcols
, i
));
9566 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
9567 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
9573 * addFkRecurseReferenced
9574 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9575 * side of the constraint
9577 * Create pg_constraint rows for the referenced side of the constraint,
9578 * referencing the parent of the referencing side; also create action triggers
9579 * on leaf partitions. If the table is partitioned, recurse to handle each
9582 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9583 * of an ALTER TABLE sequence.
9584 * fkconstraint is the constraint being added.
9585 * rel is the root referencing relation.
9586 * pkrel is the referenced relation; might be a partition, if recursing.
9587 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9588 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
9589 * top-level constraint.
9590 * numfks is the number of columns in the foreign key
9591 * pkattnum is the attnum array of referenced attributes.
9592 * fkattnum is the attnum array of referencing attributes.
9593 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9595 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9596 * NULL/DEFAULT clause
9597 * pf/pp/ffeqoperators are OID array of operators between columns.
9598 * old_check_ok signals that this constraint replaces an existing one that
9599 * was already validated (thus this one doesn't need validation).
9600 * parentDelTrigger and parentUpdTrigger, when being recursively called on
9601 * a partition, are the OIDs of the parent action triggers for DELETE and
9602 * UPDATE respectively.
9604 static ObjectAddress
9605 addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
9606 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
9608 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
9609 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
9610 int numfkdelsetcols
, int16
*fkdelsetcols
,
9612 Oid parentDelTrigger
, Oid parentUpdTrigger
)
9614 ObjectAddress address
;
9620 Oid deleteTriggerOid
,
9624 * Verify relkind for each referenced partition. At the top level, this
9625 * is redundant with a previous check, but we need it when recursing.
9627 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9628 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9630 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9631 errmsg("referenced relation \"%s\" is not a table",
9632 RelationGetRelationName(pkrel
))));
9635 * Caller supplies us with a constraint name; however, it may be used in
9636 * this partition, so come up with a different one in that case.
9638 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9639 RelationGetRelid(rel
),
9640 fkconstraint
->conname
))
9641 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
9642 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
9644 RelationGetNamespace(rel
), NIL
);
9646 conname
= fkconstraint
->conname
;
9648 if (OidIsValid(parentConstr
))
9652 connoinherit
= false;
9660 * always inherit for partitioned tables, never for legacy inheritance
9662 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
9666 * Record the FK constraint in pg_constraint.
9668 constrOid
= CreateConstraintEntry(conname
,
9669 RelationGetNamespace(rel
),
9671 fkconstraint
->deferrable
,
9672 fkconstraint
->initdeferred
,
9673 fkconstraint
->initially_valid
,
9675 RelationGetRelid(rel
),
9679 InvalidOid
, /* not a domain constraint */
9681 RelationGetRelid(pkrel
),
9687 fkconstraint
->fk_upd_action
,
9688 fkconstraint
->fk_del_action
,
9691 fkconstraint
->fk_matchtype
,
9692 NULL
, /* no exclusion constraint */
9693 NULL
, /* no check constraint */
9695 conislocal
, /* islocal */
9696 coninhcount
, /* inhcount */
9697 connoinherit
, /* conNoInherit */
9698 false); /* is_internal */
9700 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
9703 * Mark the child constraint as part of the parent constraint; it must not
9704 * be dropped on its own. (This constraint is deleted when the partition
9705 * is detached, but a special check needs to occur that the partition
9706 * contains no referenced values.)
9708 if (OidIsValid(parentConstr
))
9710 ObjectAddress referenced
;
9712 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
9713 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
9716 /* make new constraint visible, in case we add more */
9717 CommandCounterIncrement();
9720 * Create the action triggers that enforce the constraint.
9722 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
9724 constrOid
, indexOid
,
9725 parentDelTrigger
, parentUpdTrigger
,
9726 &deleteTriggerOid
, &updateTriggerOid
);
9729 * If the referenced table is partitioned, recurse on ourselves to handle
9730 * each partition. We need one pg_constraint row created for each
9731 * partition in addition to the pg_constraint row for the parent table.
9733 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9735 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
9737 for (int i
= 0; i
< pd
->nparts
; i
++)
9741 AttrNumber
*mapped_pkattnum
;
9744 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
9747 * Map the attribute numbers in the referenced side of the FK
9748 * definition to match the partition's column layout.
9750 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
9751 RelationGetDescr(pkrel
),
9755 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
9756 for (int j
= 0; j
< numfks
; j
++)
9757 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
9760 mapped_pkattnum
= pkattnum
;
9763 partIndexId
= index_get_partition(partRel
, indexOid
);
9764 if (!OidIsValid(partIndexId
))
9765 elog(ERROR
, "index for %u not found in partition %s",
9766 indexOid
, RelationGetRelationName(partRel
));
9767 addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, partRel
,
9768 partIndexId
, constrOid
, numfks
,
9769 mapped_pkattnum
, fkattnum
,
9770 pfeqoperators
, ppeqoperators
, ffeqoperators
,
9771 numfkdelsetcols
, fkdelsetcols
,
9773 deleteTriggerOid
, updateTriggerOid
);
9775 /* Done -- clean up (but keep the lock) */
9776 table_close(partRel
, NoLock
);
9779 pfree(mapped_pkattnum
);
9789 * addFkRecurseReferencing
9790 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
9792 * If the referencing relation is a plain relation, create the necessary check
9793 * triggers that implement the constraint, and set up for Phase 3 constraint
9794 * verification. If the referencing relation is a partitioned table, then
9795 * we create a pg_constraint row for it and recurse on this routine for each
9798 * We assume that the referenced relation is locked against concurrent
9799 * deletions. If it's a partitioned relation, every partition must be so
9802 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9803 * of an ALTER TABLE sequence.
9804 * fkconstraint is the constraint being added.
9805 * rel is the referencing relation; might be a partition, if recursing.
9806 * pkrel is the root referenced relation.
9807 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9808 * parentConstr is the OID of the parent constraint (there is always one).
9809 * numfks is the number of columns in the foreign key
9810 * pkattnum is the attnum array of referenced attributes.
9811 * fkattnum is the attnum array of referencing attributes.
9812 * pf/pp/ffeqoperators are OID array of operators between columns.
9813 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9815 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9816 * NULL/DEFAULT clause
9817 * old_check_ok signals that this constraint replaces an existing one that
9818 * was already validated (thus this one doesn't need validation).
9819 * lockmode is the lockmode to acquire on partitions when recursing.
9820 * parentInsTrigger and parentUpdTrigger, when being recursively called on
9821 * a partition, are the OIDs of the parent check triggers for INSERT and
9822 * UPDATE respectively.
9825 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
9826 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
9827 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
9828 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
9829 int numfkdelsetcols
, int16
*fkdelsetcols
,
9830 bool old_check_ok
, LOCKMODE lockmode
,
9831 Oid parentInsTrigger
, Oid parentUpdTrigger
)
9833 Oid insertTriggerOid
,
9836 Assert(OidIsValid(parentConstr
));
9838 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
9840 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9841 errmsg("foreign key constraints are not supported on foreign tables")));
9844 * Add the check triggers to it and, if necessary, schedule it to be
9845 * checked in Phase 3.
9847 * If the relation is partitioned, drill down to do it to its partitions.
9849 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
9850 RelationGetRelid(pkrel
),
9854 parentInsTrigger
, parentUpdTrigger
,
9855 &insertTriggerOid
, &updateTriggerOid
);
9857 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
9860 * Tell Phase 3 to check that the constraint is satisfied by existing
9861 * rows. We can skip this during table creation, when requested
9862 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
9863 * and when we're recreating a constraint following a SET DATA TYPE
9864 * operation that did not impugn its validity.
9866 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
9868 NewConstraint
*newcon
;
9869 AlteredTableInfo
*tab
;
9871 tab
= ATGetQueueEntry(wqueue
, rel
);
9873 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9874 newcon
->name
= get_constraint_name(parentConstr
);
9875 newcon
->contype
= CONSTR_FOREIGN
;
9876 newcon
->refrelid
= RelationGetRelid(pkrel
);
9877 newcon
->refindid
= indexOid
;
9878 newcon
->conid
= parentConstr
;
9879 newcon
->qual
= (Node
*) fkconstraint
;
9881 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9884 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9886 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
9890 * Triggers of the foreign keys will be manipulated a bunch of times
9891 * in the loop below. To avoid repeatedly opening/closing the trigger
9892 * catalog relation, we open it here and pass it to the subroutines
9895 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
9898 * Recurse to take appropriate action on each partition; either we
9899 * find an existing constraint to reparent to ours, or we create a new
9902 for (int i
= 0; i
< pd
->nparts
; i
++)
9904 Oid partitionId
= pd
->oids
[i
];
9905 Relation partition
= table_open(partitionId
, lockmode
);
9908 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
9912 ObjectAddress address
,
9916 CheckTableNotInUse(partition
, "ALTER TABLE");
9918 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
9919 RelationGetDescr(rel
),
9921 for (int j
= 0; j
< numfks
; j
++)
9922 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
9924 /* Check whether an existing constraint can be repurposed */
9925 partFKs
= copyObject(RelationGetFKeyList(partition
));
9927 foreach(cell
, partFKs
)
9929 ForeignKeyCacheInfo
*fk
;
9931 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
9932 if (tryAttachPartitionForeignKey(fk
,
9949 table_close(partition
, NoLock
);
9954 * No luck finding a good constraint to reuse; create our own.
9956 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9957 RelationGetRelid(partition
),
9958 fkconstraint
->conname
))
9959 conname
= ChooseConstraintName(RelationGetRelationName(partition
),
9960 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
9962 RelationGetNamespace(partition
), NIL
);
9964 conname
= fkconstraint
->conname
;
9966 CreateConstraintEntry(conname
,
9967 RelationGetNamespace(partition
),
9969 fkconstraint
->deferrable
,
9970 fkconstraint
->initdeferred
,
9971 fkconstraint
->initially_valid
,
9979 RelationGetRelid(pkrel
),
9985 fkconstraint
->fk_upd_action
,
9986 fkconstraint
->fk_del_action
,
9989 fkconstraint
->fk_matchtype
,
9999 * Give this constraint partition-type dependencies on the parent
10000 * constraint as well as the table.
10002 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10003 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10004 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10005 ObjectAddressSet(referenced
, RelationRelationId
, partitionId
);
10006 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10008 /* Make all this visible before recursing */
10009 CommandCounterIncrement();
10011 /* call ourselves to finalize the creation and we're done */
10012 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10028 table_close(partition
, NoLock
);
10031 table_close(trigrel
, RowExclusiveLock
);
10036 * CloneForeignKeyConstraints
10037 * Clone foreign keys from a partitioned table to a newly acquired
10040 * partitionRel is a partition of parentRel, so we can be certain that it has
10041 * the same columns with the same datatypes. The columns may be in different
10044 * wqueue must be passed to set up phase 3 constraint checking, unless the
10045 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10049 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10050 Relation partitionRel
)
10052 /* This only works for declarative partitioning */
10053 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10056 * Clone constraints for which the parent is on the referenced side.
10058 CloneFkReferenced(parentRel
, partitionRel
);
10061 * Now clone constraints where the parent is on the referencing side.
10063 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10067 * CloneFkReferenced
10068 * Subroutine for CloneForeignKeyConstraints
10070 * Find all the FKs that have the parent relation on the referenced side;
10071 * clone those constraints to the given partition. This is to be called
10072 * when the partition is being created or attached.
10074 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10076 * This recurses to partitions, if the relation being attached is partitioned.
10077 * Recursion is done by calling addFkRecurseReferenced.
10080 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10082 Relation pg_constraint
;
10086 ScanKeyData key
[2];
10092 * Search for any constraints where this partition's parent is in the
10093 * referenced side. However, we must not clone any constraint whose
10094 * parent constraint is also going to be cloned, to avoid duplicates. So
10095 * do it in two steps: first construct the list of constraints to clone,
10096 * then go over that list cloning those whose parents are not in the list.
10097 * (We must not rely on the parent being seen first, since the catalog
10098 * scan could return children first.)
10100 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
10101 ScanKeyInit(&key
[0],
10102 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
10103 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
10104 ScanKeyInit(&key
[1],
10105 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
10106 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
10107 /* This is a seqscan, as we don't have a usable index ... */
10108 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
10110 while ((tuple
= systable_getnext(scan
)) != NULL
)
10112 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10114 clone
= lappend_oid(clone
, constrForm
->oid
);
10116 systable_endscan(scan
);
10117 table_close(pg_constraint
, RowShareLock
);
10120 * Triggers of the foreign keys will be manipulated a bunch of times in
10121 * the loop below. To avoid repeatedly opening/closing the trigger
10122 * catalog relation, we open it here and pass it to the subroutines called
10125 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10127 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
10128 RelationGetDescr(parentRel
),
10130 foreach(cell
, clone
)
10132 Oid constrOid
= lfirst_oid(cell
);
10133 Form_pg_constraint constrForm
;
10138 AttrNumber conkey
[INDEX_MAX_KEYS
];
10139 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
10140 AttrNumber confkey
[INDEX_MAX_KEYS
];
10141 Oid conpfeqop
[INDEX_MAX_KEYS
];
10142 Oid conppeqop
[INDEX_MAX_KEYS
];
10143 Oid conffeqop
[INDEX_MAX_KEYS
];
10144 int numfkdelsetcols
;
10145 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10146 Constraint
*fkconstraint
;
10147 Oid deleteTriggerOid
,
10150 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
10151 if (!HeapTupleIsValid(tuple
))
10152 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
10153 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10156 * As explained above: don't try to clone a constraint for which we're
10157 * going to clone the parent.
10159 if (list_member_oid(clone
, constrForm
->conparentid
))
10161 ReleaseSysCache(tuple
);
10166 * Don't clone self-referencing foreign keys, which can be in the
10167 * partitioned table or in the partition-to-be.
10169 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
10170 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
10172 ReleaseSysCache(tuple
);
10177 * Because we're only expanding the key space at the referenced side,
10178 * we don't need to prevent any operation in the referencing table, so
10179 * AccessShareLock suffices (assumes that dropping the constraint
10182 fkRel
= table_open(constrForm
->conrelid
, AccessShareLock
);
10184 indexOid
= constrForm
->conindid
;
10185 DeconstructFkConstraintRow(tuple
,
10195 for (int i
= 0; i
< numfks
; i
++)
10196 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
10198 fkconstraint
= makeNode(Constraint
);
10199 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10200 fkconstraint
->conname
= NameStr(constrForm
->conname
);
10201 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10202 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10203 fkconstraint
->location
= -1;
10204 fkconstraint
->pktable
= NULL
;
10205 /* ->fk_attrs determined below */
10206 fkconstraint
->pk_attrs
= NIL
;
10207 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10208 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10209 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10210 fkconstraint
->fk_del_set_cols
= NIL
;
10211 fkconstraint
->old_conpfeqop
= NIL
;
10212 fkconstraint
->old_pktable_oid
= InvalidOid
;
10213 fkconstraint
->skip_validation
= false;
10214 fkconstraint
->initially_valid
= true;
10216 /* set up colnames that are used to generate the constraint name */
10217 for (int i
= 0; i
< numfks
; i
++)
10219 Form_pg_attribute att
;
10221 att
= TupleDescAttr(RelationGetDescr(fkRel
),
10223 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10224 makeString(NameStr(att
->attname
)));
10228 * Add the new foreign key constraint pointing to the new partition.
10229 * Because this new partition appears in the referenced side of the
10230 * constraint, we don't need to set up for Phase 3 check.
10232 partIndexId
= index_get_partition(partitionRel
, indexOid
);
10233 if (!OidIsValid(partIndexId
))
10234 elog(ERROR
, "index for %u not found in partition %s",
10235 indexOid
, RelationGetRelationName(partitionRel
));
10238 * Get the "action" triggers belonging to the constraint to pass as
10239 * parent OIDs for similar triggers that will be created on the
10240 * partition in addFkRecurseReferenced().
10242 GetForeignKeyActionTriggers(trigrel
, constrOid
,
10243 constrForm
->confrelid
, constrForm
->conrelid
,
10244 &deleteTriggerOid
, &updateTriggerOid
);
10246 addFkRecurseReferenced(NULL
,
10264 table_close(fkRel
, NoLock
);
10265 ReleaseSysCache(tuple
);
10268 table_close(trigrel
, RowExclusiveLock
);
10272 * CloneFkReferencing
10273 * Subroutine for CloneForeignKeyConstraints
10275 * For each FK constraint of the parent relation in the given list, find an
10276 * equivalent constraint in its partition relation that can be reparented;
10277 * if one cannot be found, create a new constraint in the partition as its
10280 * If wqueue is given, it is used to set up phase-3 verification for each
10281 * cloned constraint; if omitted, we assume that such verification is not
10282 * needed (example: the partition is being created anew).
10285 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
10293 /* obtain a list of constraints that we need to clone */
10294 foreach(cell
, RelationGetFKeyList(parentRel
))
10296 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
10298 clone
= lappend_oid(clone
, fk
->conoid
);
10302 * Silently do nothing if there's nothing to do. In particular, this
10303 * avoids throwing a spurious error for foreign tables.
10308 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10310 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10311 errmsg("foreign key constraints are not supported on foreign tables")));
10314 * Triggers of the foreign keys will be manipulated a bunch of times in
10315 * the loop below. To avoid repeatedly opening/closing the trigger
10316 * catalog relation, we open it here and pass it to the subroutines called
10319 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10322 * The constraint key may differ, if the columns in the partition are
10323 * different. This map is used to convert them.
10325 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
10326 RelationGetDescr(parentRel
),
10329 partFKs
= copyObject(RelationGetFKeyList(partRel
));
10331 foreach(cell
, clone
)
10333 Oid parentConstrOid
= lfirst_oid(cell
);
10334 Form_pg_constraint constrForm
;
10338 AttrNumber conkey
[INDEX_MAX_KEYS
];
10339 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
10340 AttrNumber confkey
[INDEX_MAX_KEYS
];
10341 Oid conpfeqop
[INDEX_MAX_KEYS
];
10342 Oid conppeqop
[INDEX_MAX_KEYS
];
10343 Oid conffeqop
[INDEX_MAX_KEYS
];
10344 int numfkdelsetcols
;
10345 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10346 Constraint
*fkconstraint
;
10350 ObjectAddress address
,
10353 Oid insertTriggerOid
,
10356 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
10357 if (!HeapTupleIsValid(tuple
))
10358 elog(ERROR
, "cache lookup failed for constraint %u",
10360 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10362 /* Don't clone constraints whose parents are being cloned */
10363 if (list_member_oid(clone
, constrForm
->conparentid
))
10365 ReleaseSysCache(tuple
);
10370 * Need to prevent concurrent deletions. If pkrel is a partitioned
10371 * relation, that means to lock all partitions.
10373 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
10374 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10375 (void) find_all_inheritors(RelationGetRelid(pkrel
),
10376 ShareRowExclusiveLock
, NULL
);
10378 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
10379 conpfeqop
, conppeqop
, conffeqop
,
10380 &numfkdelsetcols
, confdelsetcols
);
10381 for (int i
= 0; i
< numfks
; i
++)
10382 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
10385 * Get the "check" triggers belonging to the constraint to pass as
10386 * parent OIDs for similar triggers that will be created on the
10387 * partition in addFkRecurseReferencing(). They are also passed to
10388 * tryAttachPartitionForeignKey() below to simply assign as parents to
10389 * the partition's existing "check" triggers, that is, if the
10390 * corresponding constraints is deemed attachable to the parent
10393 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
10394 constrForm
->confrelid
, constrForm
->conrelid
,
10395 &insertTriggerOid
, &updateTriggerOid
);
10398 * Before creating a new constraint, see whether any existing FKs are
10399 * fit for the purpose. If one is, attach the parent constraint to
10400 * it, and don't clone anything. This way we avoid the expensive
10401 * verification step and don't end up with a duplicate FK, and we
10402 * don't need to recurse to partitions for this constraint.
10405 foreach(lc
, partFKs
)
10407 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
10409 if (tryAttachPartitionForeignKey(fk
,
10410 RelationGetRelid(partRel
),
10421 table_close(pkrel
, NoLock
);
10427 ReleaseSysCache(tuple
);
10431 /* No dice. Set up to create our own constraint */
10432 fkconstraint
= makeNode(Constraint
);
10433 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10434 /* ->conname determined below */
10435 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10436 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10437 fkconstraint
->location
= -1;
10438 fkconstraint
->pktable
= NULL
;
10439 /* ->fk_attrs determined below */
10440 fkconstraint
->pk_attrs
= NIL
;
10441 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10442 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10443 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10444 fkconstraint
->fk_del_set_cols
= NIL
;
10445 fkconstraint
->old_conpfeqop
= NIL
;
10446 fkconstraint
->old_pktable_oid
= InvalidOid
;
10447 fkconstraint
->skip_validation
= false;
10448 fkconstraint
->initially_valid
= true;
10449 for (int i
= 0; i
< numfks
; i
++)
10451 Form_pg_attribute att
;
10453 att
= TupleDescAttr(RelationGetDescr(partRel
),
10454 mapped_conkey
[i
] - 1);
10455 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10456 makeString(NameStr(att
->attname
)));
10458 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10459 RelationGetRelid(partRel
),
10460 NameStr(constrForm
->conname
)))
10461 fkconstraint
->conname
=
10462 ChooseConstraintName(RelationGetRelationName(partRel
),
10463 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10465 RelationGetNamespace(partRel
), NIL
);
10467 fkconstraint
->conname
= pstrdup(NameStr(constrForm
->conname
));
10469 indexOid
= constrForm
->conindid
;
10471 CreateConstraintEntry(fkconstraint
->conname
,
10472 constrForm
->connamespace
,
10473 CONSTRAINT_FOREIGN
,
10474 fkconstraint
->deferrable
,
10475 fkconstraint
->initdeferred
,
10476 constrForm
->convalidated
,
10478 RelationGetRelid(partRel
),
10482 InvalidOid
, /* not a domain constraint */
10484 constrForm
->confrelid
, /* same foreign rel */
10490 fkconstraint
->fk_upd_action
,
10491 fkconstraint
->fk_del_action
,
10494 fkconstraint
->fk_matchtype
,
10498 false, /* islocal */
10500 false, /* conNoInherit */
10503 /* Set up partition dependencies for the new constraint */
10504 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10505 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstrOid
);
10506 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10507 ObjectAddressSet(referenced
, RelationRelationId
,
10508 RelationGetRelid(partRel
));
10509 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10511 /* Done with the cloned constraint's tuple */
10512 ReleaseSysCache(tuple
);
10514 /* Make all this visible before recursing */
10515 CommandCounterIncrement();
10517 addFkRecurseReferencing(wqueue
,
10531 false, /* no old check exists */
10532 AccessExclusiveLock
,
10535 table_close(pkrel
, NoLock
);
10538 table_close(trigrel
, RowExclusiveLock
);
10542 * When the parent of a partition receives [the referencing side of] a foreign
10543 * key, we must propagate that foreign key to the partition. However, the
10544 * partition might already have an equivalent foreign key; this routine
10545 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10546 * by the other parameters. If they are equivalent, create the link between
10547 * the two constraints and return true.
10549 * If the given FK does not match the one defined by rest of the params,
10553 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
10555 Oid parentConstrOid
,
10557 AttrNumber
*mapped_conkey
,
10558 AttrNumber
*confkey
,
10560 Oid parentInsTrigger
,
10561 Oid parentUpdTrigger
,
10564 HeapTuple parentConstrTup
;
10565 Form_pg_constraint parentConstr
;
10566 HeapTuple partcontup
;
10567 Form_pg_constraint partConstr
;
10571 Oid insertTriggerOid
,
10574 parentConstrTup
= SearchSysCache1(CONSTROID
,
10575 ObjectIdGetDatum(parentConstrOid
));
10576 if (!HeapTupleIsValid(parentConstrTup
))
10577 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
10578 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
10581 * Do some quick & easy initial checks. If any of these fail, we cannot
10582 * use this constraint.
10584 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
10586 ReleaseSysCache(parentConstrTup
);
10589 for (int i
= 0; i
< numfks
; i
++)
10591 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
10592 fk
->confkey
[i
] != confkey
[i
] ||
10593 fk
->conpfeqop
[i
] != conpfeqop
[i
])
10595 ReleaseSysCache(parentConstrTup
);
10601 * Looks good so far; do some more extensive checks. Presumably the check
10602 * for 'convalidated' could be dropped, since we don't really care about
10603 * that, but let's be careful for now.
10605 partcontup
= SearchSysCache1(CONSTROID
,
10606 ObjectIdGetDatum(fk
->conoid
));
10607 if (!HeapTupleIsValid(partcontup
))
10608 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
10609 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
10610 if (OidIsValid(partConstr
->conparentid
) ||
10611 !partConstr
->convalidated
||
10612 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
10613 partConstr
->condeferred
!= parentConstr
->condeferred
||
10614 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
10615 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
10616 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
10618 ReleaseSysCache(parentConstrTup
);
10619 ReleaseSysCache(partcontup
);
10623 ReleaseSysCache(partcontup
);
10624 ReleaseSysCache(parentConstrTup
);
10627 * Looks good! Attach this constraint. The action triggers in the new
10628 * partition become redundant -- the parent table already has equivalent
10629 * ones, and those will be able to reach the partition. Remove the ones
10630 * in the partition. We identify them because they have our constraint
10631 * OID, as well as being on the referenced rel.
10634 Anum_pg_trigger_tgconstraint
,
10635 BTEqualStrategyNumber
, F_OIDEQ
,
10636 ObjectIdGetDatum(fk
->conoid
));
10637 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
10639 while ((trigtup
= systable_getnext(scan
)) != NULL
)
10641 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
10642 ObjectAddress trigger
;
10644 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
10646 if (trgform
->tgrelid
!= fk
->confrelid
)
10650 * The constraint is originally set up to contain this trigger as an
10651 * implementation object, so there's a dependency record that links
10652 * the two; however, since the trigger is no longer needed, we remove
10653 * the dependency link in order to be able to drop the trigger while
10654 * keeping the constraint intact.
10656 deleteDependencyRecordsFor(TriggerRelationId
,
10659 /* make dependency deletion visible to performDeletion */
10660 CommandCounterIncrement();
10661 ObjectAddressSet(trigger
, TriggerRelationId
,
10663 performDeletion(&trigger
, DROP_RESTRICT
, 0);
10664 /* make trigger drop visible, in case the loop iterates */
10665 CommandCounterIncrement();
10668 systable_endscan(scan
);
10670 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
10673 * Like the constraint, attach partition's "check" triggers to the
10674 * corresponding parent triggers.
10676 GetForeignKeyCheckTriggers(trigrel
,
10677 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
10678 &insertTriggerOid
, &updateTriggerOid
);
10679 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
10680 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
10682 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
10683 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
10686 CommandCounterIncrement();
10691 * GetForeignKeyActionTriggers
10692 * Returns delete and update "action" triggers of the given relation
10693 * belonging to the given constraint
10696 GetForeignKeyActionTriggers(Relation trigrel
,
10697 Oid conoid
, Oid confrelid
, Oid conrelid
,
10698 Oid
*deleteTriggerOid
,
10699 Oid
*updateTriggerOid
)
10705 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
10707 Anum_pg_trigger_tgconstraint
,
10708 BTEqualStrategyNumber
, F_OIDEQ
,
10709 ObjectIdGetDatum(conoid
));
10711 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
10713 while ((trigtup
= systable_getnext(scan
)) != NULL
)
10715 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
10717 if (trgform
->tgconstrrelid
!= conrelid
)
10719 if (trgform
->tgrelid
!= confrelid
)
10721 /* Only ever look at "action" triggers on the PK side. */
10722 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
10724 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
10726 Assert(*deleteTriggerOid
== InvalidOid
);
10727 *deleteTriggerOid
= trgform
->oid
;
10729 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
10731 Assert(*updateTriggerOid
== InvalidOid
);
10732 *updateTriggerOid
= trgform
->oid
;
10734 #ifndef USE_ASSERT_CHECKING
10735 /* In an assert-enabled build, continue looking to find duplicates */
10736 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
10741 if (!OidIsValid(*deleteTriggerOid
))
10742 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
10744 if (!OidIsValid(*updateTriggerOid
))
10745 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
10748 systable_endscan(scan
);
10752 * GetForeignKeyCheckTriggers
10753 * Returns insert and update "check" triggers of the given relation
10754 * belonging to the given constraint
10757 GetForeignKeyCheckTriggers(Relation trigrel
,
10758 Oid conoid
, Oid confrelid
, Oid conrelid
,
10759 Oid
*insertTriggerOid
,
10760 Oid
*updateTriggerOid
)
10766 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
10768 Anum_pg_trigger_tgconstraint
,
10769 BTEqualStrategyNumber
, F_OIDEQ
,
10770 ObjectIdGetDatum(conoid
));
10772 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
10774 while ((trigtup
= systable_getnext(scan
)) != NULL
)
10776 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
10778 if (trgform
->tgconstrrelid
!= confrelid
)
10780 if (trgform
->tgrelid
!= conrelid
)
10782 /* Only ever look at "check" triggers on the FK side. */
10783 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
10785 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
10787 Assert(*insertTriggerOid
== InvalidOid
);
10788 *insertTriggerOid
= trgform
->oid
;
10790 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
10792 Assert(*updateTriggerOid
== InvalidOid
);
10793 *updateTriggerOid
= trgform
->oid
;
10795 #ifndef USE_ASSERT_CHECKING
10796 /* In an assert-enabled build, continue looking to find duplicates. */
10797 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
10802 if (!OidIsValid(*insertTriggerOid
))
10803 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
10805 if (!OidIsValid(*updateTriggerOid
))
10806 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
10809 systable_endscan(scan
);
10813 * ALTER TABLE ALTER CONSTRAINT
10815 * Update the attributes of a constraint.
10817 * Currently only works for Foreign Key constraints.
10819 * If the constraint is modified, returns its address; otherwise, return
10820 * InvalidObjectAddress.
10822 static ObjectAddress
10823 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
10824 bool recursing
, LOCKMODE lockmode
)
10826 Constraint
*cmdcon
;
10830 ScanKeyData skey
[3];
10831 HeapTuple contuple
;
10832 Form_pg_constraint currcon
;
10833 ObjectAddress address
;
10834 List
*otherrelids
= NIL
;
10837 cmdcon
= castNode(Constraint
, cmd
->def
);
10839 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
10840 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10843 * Find and check the target constraint
10845 ScanKeyInit(&skey
[0],
10846 Anum_pg_constraint_conrelid
,
10847 BTEqualStrategyNumber
, F_OIDEQ
,
10848 ObjectIdGetDatum(RelationGetRelid(rel
)));
10849 ScanKeyInit(&skey
[1],
10850 Anum_pg_constraint_contypid
,
10851 BTEqualStrategyNumber
, F_OIDEQ
,
10852 ObjectIdGetDatum(InvalidOid
));
10853 ScanKeyInit(&skey
[2],
10854 Anum_pg_constraint_conname
,
10855 BTEqualStrategyNumber
, F_NAMEEQ
,
10856 CStringGetDatum(cmdcon
->conname
));
10857 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
10858 true, NULL
, 3, skey
);
10860 /* There can be at most one matching row */
10861 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
10863 (errcode(ERRCODE_UNDEFINED_OBJECT
),
10864 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10865 cmdcon
->conname
, RelationGetRelationName(rel
))));
10867 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
10868 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
10870 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10871 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
10872 cmdcon
->conname
, RelationGetRelationName(rel
))));
10875 * If it's not the topmost constraint, raise an error.
10877 * Altering a non-topmost constraint leaves some triggers untouched, since
10878 * they are not directly connected to this constraint; also, pg_dump would
10879 * ignore the deferrability status of the individual constraint, since it
10880 * only dumps topmost constraints. Avoid these problems by refusing this
10881 * operation and telling the user to alter the parent constraint instead.
10883 if (OidIsValid(currcon
->conparentid
))
10886 Oid parent
= currcon
->conparentid
;
10887 char *ancestorname
= NULL
;
10888 char *ancestortable
= NULL
;
10890 /* Loop to find the topmost constraint */
10891 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
10893 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
10895 /* If no parent, this is the constraint we want */
10896 if (!OidIsValid(contup
->conparentid
))
10898 ancestorname
= pstrdup(NameStr(contup
->conname
));
10899 ancestortable
= get_rel_name(contup
->conrelid
);
10900 ReleaseSysCache(tp
);
10904 parent
= contup
->conparentid
;
10905 ReleaseSysCache(tp
);
10909 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
10910 cmdcon
->conname
, RelationGetRelationName(rel
)),
10911 ancestorname
&& ancestortable
?
10912 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
10913 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
10914 errhint("You may alter the constraint it derives from instead.")));
10918 * Do the actual catalog work. We can skip changing if already in the
10919 * desired state, but not if a partitioned table: partitions need to be
10920 * processed regardless, in case they had the constraint locally changed.
10922 address
= InvalidObjectAddress
;
10923 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
10924 currcon
->condeferred
!= cmdcon
->initdeferred
||
10925 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10927 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
10928 &otherrelids
, lockmode
))
10929 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
10933 * ATExecAlterConstrRecurse already invalidated relcache for the relations
10934 * having the constraint itself; here we also invalidate for relations
10935 * that have any triggers that are part of the constraint.
10937 foreach(lc
, otherrelids
)
10938 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
10940 systable_endscan(scan
);
10942 table_close(tgrel
, RowExclusiveLock
);
10943 table_close(conrel
, RowExclusiveLock
);
10949 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
10950 * constraint is altered.
10952 * *otherrelids is appended OIDs of relations containing affected triggers.
10954 * Note that we must recurse even when the values are correct, in case
10955 * indirect descendants have had their constraints altered locally.
10956 * (This could be avoided if we forbade altering constraints in partitions
10957 * but existing releases don't do that.)
10960 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
10961 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
10964 Form_pg_constraint currcon
;
10967 bool changed
= false;
10969 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
10970 conoid
= currcon
->oid
;
10971 refrelid
= currcon
->confrelid
;
10974 * Update pg_constraint with the flags from cmdcon.
10976 * If called to modify a constraint that's already in the desired state,
10977 * silently do nothing.
10979 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
10980 currcon
->condeferred
!= cmdcon
->initdeferred
)
10982 HeapTuple copyTuple
;
10983 Form_pg_constraint copy_con
;
10986 SysScanDesc tgscan
;
10988 copyTuple
= heap_copytuple(contuple
);
10989 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
10990 copy_con
->condeferrable
= cmdcon
->deferrable
;
10991 copy_con
->condeferred
= cmdcon
->initdeferred
;
10992 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
10994 InvokeObjectPostAlterHook(ConstraintRelationId
,
10997 heap_freetuple(copyTuple
);
11000 /* Make new constraint flags visible to others */
11001 CacheInvalidateRelcache(rel
);
11004 * Now we need to update the multiple entries in pg_trigger that
11005 * implement the constraint.
11007 ScanKeyInit(&tgkey
,
11008 Anum_pg_trigger_tgconstraint
,
11009 BTEqualStrategyNumber
, F_OIDEQ
,
11010 ObjectIdGetDatum(conoid
));
11011 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11013 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11015 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11016 Form_pg_trigger copy_tg
;
11017 HeapTuple tgCopyTuple
;
11020 * Remember OIDs of other relation(s) involved in FK constraint.
11021 * (Note: it's likely that we could skip forcing a relcache inval
11022 * for other rels that don't have a trigger whose properties
11023 * change, but let's be conservative.)
11025 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11026 *otherrelids
= list_append_unique_oid(*otherrelids
,
11030 * Update deferrability of RI_FKey_noaction_del,
11031 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11032 * triggers, but not others; see createForeignKeyActionTriggers
11033 * and CreateFKCheckTrigger.
11035 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11036 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11037 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11038 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11041 tgCopyTuple
= heap_copytuple(tgtuple
);
11042 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11044 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11045 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11046 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11048 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11050 heap_freetuple(tgCopyTuple
);
11053 systable_endscan(tgscan
);
11057 * If the table at either end of the constraint is partitioned, we need to
11058 * recurse and handle every constraint that is a child of this one.
11060 * (This assumes that the recurse flag is forcibly set for partitioned
11061 * tables, and not set for legacy inheritance, though we don't check for
11064 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11065 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11069 HeapTuple childtup
;
11072 Anum_pg_constraint_conparentid
,
11073 BTEqualStrategyNumber
, F_OIDEQ
,
11074 ObjectIdGetDatum(conoid
));
11076 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11077 true, NULL
, 1, &pkey
);
11079 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11081 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11084 childrel
= table_open(childcon
->conrelid
, lockmode
);
11085 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11086 otherrelids
, lockmode
);
11087 table_close(childrel
, NoLock
);
11090 systable_endscan(pscan
);
11097 * ALTER TABLE VALIDATE CONSTRAINT
11099 * XXX The reason we handle recursion here rather than at Phase 1 is because
11100 * there's no good way to skip recursing when handling foreign keys: there is
11101 * no need to lock children in that case, yet we wouldn't be able to avoid
11102 * doing so at that level.
11104 * Return value is the address of the validated constraint. If the constraint
11105 * was already validated, InvalidObjectAddress is returned.
11107 static ObjectAddress
11108 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
11109 bool recurse
, bool recursing
, LOCKMODE lockmode
)
11113 ScanKeyData skey
[3];
11115 Form_pg_constraint con
;
11116 ObjectAddress address
;
11118 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11121 * Find and check the target constraint
11123 ScanKeyInit(&skey
[0],
11124 Anum_pg_constraint_conrelid
,
11125 BTEqualStrategyNumber
, F_OIDEQ
,
11126 ObjectIdGetDatum(RelationGetRelid(rel
)));
11127 ScanKeyInit(&skey
[1],
11128 Anum_pg_constraint_contypid
,
11129 BTEqualStrategyNumber
, F_OIDEQ
,
11130 ObjectIdGetDatum(InvalidOid
));
11131 ScanKeyInit(&skey
[2],
11132 Anum_pg_constraint_conname
,
11133 BTEqualStrategyNumber
, F_NAMEEQ
,
11134 CStringGetDatum(constrName
));
11135 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11136 true, NULL
, 3, skey
);
11138 /* There can be at most one matching row */
11139 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
11141 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11142 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11143 constrName
, RelationGetRelationName(rel
))));
11145 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11146 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
11147 con
->contype
!= CONSTRAINT_CHECK
)
11149 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11150 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11151 constrName
, RelationGetRelationName(rel
))));
11153 if (!con
->convalidated
)
11155 AlteredTableInfo
*tab
;
11156 HeapTuple copyTuple
;
11157 Form_pg_constraint copy_con
;
11159 if (con
->contype
== CONSTRAINT_FOREIGN
)
11161 NewConstraint
*newcon
;
11162 Constraint
*fkconstraint
;
11164 /* Queue validation for phase 3 */
11165 fkconstraint
= makeNode(Constraint
);
11166 /* for now this is all we need */
11167 fkconstraint
->conname
= constrName
;
11169 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11170 newcon
->name
= constrName
;
11171 newcon
->contype
= CONSTR_FOREIGN
;
11172 newcon
->refrelid
= con
->confrelid
;
11173 newcon
->refindid
= con
->conindid
;
11174 newcon
->conid
= con
->oid
;
11175 newcon
->qual
= (Node
*) fkconstraint
;
11177 /* Find or create work queue entry for this table */
11178 tab
= ATGetQueueEntry(wqueue
, rel
);
11179 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11182 * We disallow creating invalid foreign keys to or from
11183 * partitioned tables, so ignoring the recursion bit is okay.
11186 else if (con
->contype
== CONSTRAINT_CHECK
)
11188 List
*children
= NIL
;
11190 NewConstraint
*newcon
;
11195 * If we're recursing, the parent has already done this, so skip
11196 * it. Also, if the constraint is a NO INHERIT constraint, we
11197 * shouldn't try to look for it in the children.
11199 if (!recursing
&& !con
->connoinherit
)
11200 children
= find_all_inheritors(RelationGetRelid(rel
),
11204 * For CHECK constraints, we must ensure that we only mark the
11205 * constraint as validated on the parent if it's already validated
11208 * We recurse before validating on the parent, to reduce risk of
11211 foreach(child
, children
)
11213 Oid childoid
= lfirst_oid(child
);
11216 if (childoid
== RelationGetRelid(rel
))
11220 * If we are told not to recurse, there had better not be any
11221 * child tables, because we can't mark the constraint on the
11222 * parent valid unless it is valid for all child tables.
11226 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
11227 errmsg("constraint must be validated on child tables too")));
11229 /* find_all_inheritors already got lock */
11230 childrel
= table_open(childoid
, NoLock
);
11232 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
11234 table_close(childrel
, NoLock
);
11237 /* Queue validation for phase 3 */
11238 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11239 newcon
->name
= constrName
;
11240 newcon
->contype
= CONSTR_CHECK
;
11241 newcon
->refrelid
= InvalidOid
;
11242 newcon
->refindid
= InvalidOid
;
11243 newcon
->conid
= con
->oid
;
11245 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
11246 Anum_pg_constraint_conbin
);
11247 conbin
= TextDatumGetCString(val
);
11248 newcon
->qual
= (Node
*) stringToNode(conbin
);
11250 /* Find or create work queue entry for this table */
11251 tab
= ATGetQueueEntry(wqueue
, rel
);
11252 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11255 * Invalidate relcache so that others see the new validated
11258 CacheInvalidateRelcache(rel
);
11262 * Now update the catalog, while we have the door open.
11264 copyTuple
= heap_copytuple(tuple
);
11265 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11266 copy_con
->convalidated
= true;
11267 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11269 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
11271 heap_freetuple(copyTuple
);
11273 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
11276 address
= InvalidObjectAddress
; /* already validated */
11278 systable_endscan(scan
);
11280 table_close(conrel
, RowExclusiveLock
);
11287 * transformColumnNameList - transform list of column names
11289 * Lookup each name and return its attnum and, optionally, type OID
11291 * Note: the name of this function suggests that it's general-purpose,
11292 * but actually it's only used to look up names appearing in foreign-key
11293 * clauses. The error messages would need work to use it in other cases,
11294 * and perhaps the validity checks as well.
11297 transformColumnNameList(Oid relId
, List
*colList
,
11298 int16
*attnums
, Oid
*atttypids
)
11304 foreach(l
, colList
)
11306 char *attname
= strVal(lfirst(l
));
11307 HeapTuple atttuple
;
11308 Form_pg_attribute attform
;
11310 atttuple
= SearchSysCacheAttName(relId
, attname
);
11311 if (!HeapTupleIsValid(atttuple
))
11313 (errcode(ERRCODE_UNDEFINED_COLUMN
),
11314 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11316 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
11317 if (attform
->attnum
< 0)
11319 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
11320 errmsg("system columns cannot be used in foreign keys")));
11321 if (attnum
>= INDEX_MAX_KEYS
)
11323 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
11324 errmsg("cannot have more than %d keys in a foreign key",
11326 attnums
[attnum
] = attform
->attnum
;
11327 if (atttypids
!= NULL
)
11328 atttypids
[attnum
] = attform
->atttypid
;
11329 ReleaseSysCache(atttuple
);
11337 * transformFkeyGetPrimaryKey -
11339 * Look up the names, attnums, and types of the primary key attributes
11340 * for the pkrel. Also return the index OID and index opclasses of the
11341 * index supporting the primary key.
11343 * All parameters except pkrel are output parameters. Also, the function
11344 * return value is the number of attributes in the primary key.
11346 * Used when the column list in the REFERENCES specification is omitted.
11349 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
11350 List
**attnamelist
,
11351 int16
*attnums
, Oid
*atttypids
,
11354 List
*indexoidlist
;
11355 ListCell
*indexoidscan
;
11356 HeapTuple indexTuple
= NULL
;
11357 Form_pg_index indexStruct
= NULL
;
11358 Datum indclassDatum
;
11359 oidvector
*indclass
;
11363 * Get the list of index OIDs for the table from the relcache, and look up
11364 * each one in the pg_index syscache until we find one marked primary key
11365 * (hopefully there isn't more than one such). Insist it's valid, too.
11367 *indexOid
= InvalidOid
;
11369 indexoidlist
= RelationGetIndexList(pkrel
);
11371 foreach(indexoidscan
, indexoidlist
)
11373 Oid indexoid
= lfirst_oid(indexoidscan
);
11375 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11376 if (!HeapTupleIsValid(indexTuple
))
11377 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11378 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11379 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
11382 * Refuse to use a deferrable primary key. This is per SQL spec,
11383 * and there would be a lot of interesting semantic problems if we
11384 * tried to allow it.
11386 if (!indexStruct
->indimmediate
)
11388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11389 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11390 RelationGetRelationName(pkrel
))));
11392 *indexOid
= indexoid
;
11395 ReleaseSysCache(indexTuple
);
11398 list_free(indexoidlist
);
11401 * Check that we found it
11403 if (!OidIsValid(*indexOid
))
11405 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11406 errmsg("there is no primary key for referenced table \"%s\"",
11407 RelationGetRelationName(pkrel
))));
11409 /* Must get indclass the hard way */
11410 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11411 Anum_pg_index_indclass
);
11412 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11415 * Now build the list of PK attributes from the indkey definition (we
11416 * assume a primary key cannot have expressional elements)
11418 *attnamelist
= NIL
;
11419 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
11421 int pkattno
= indexStruct
->indkey
.values
[i
];
11423 attnums
[i
] = pkattno
;
11424 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
11425 opclasses
[i
] = indclass
->values
[i
];
11426 *attnamelist
= lappend(*attnamelist
,
11427 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
11430 ReleaseSysCache(indexTuple
);
11436 * transformFkeyCheckAttrs -
11438 * Make sure that the attributes of a referenced table belong to a unique
11439 * (or primary key) constraint. Return the OID of the index supporting
11440 * the constraint, as well as the opclasses associated with the index
11444 transformFkeyCheckAttrs(Relation pkrel
,
11445 int numattrs
, int16
*attnums
,
11446 Oid
*opclasses
) /* output parameter */
11448 Oid indexoid
= InvalidOid
;
11449 bool found
= false;
11450 bool found_deferrable
= false;
11451 List
*indexoidlist
;
11452 ListCell
*indexoidscan
;
11457 * Reject duplicate appearances of columns in the referenced-columns list.
11458 * Such a case is forbidden by the SQL standard, and even if we thought it
11459 * useful to allow it, there would be ambiguity about how to match the
11460 * list to unique indexes (in particular, it'd be unclear which index
11461 * opclass goes with which FK column).
11463 for (i
= 0; i
< numattrs
; i
++)
11465 for (j
= i
+ 1; j
< numattrs
; j
++)
11467 if (attnums
[i
] == attnums
[j
])
11469 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
11470 errmsg("foreign key referenced-columns list must not contain duplicates")));
11475 * Get the list of index OIDs for the table from the relcache, and look up
11476 * each one in the pg_index syscache, and match unique indexes to the list
11477 * of attnums we are given.
11479 indexoidlist
= RelationGetIndexList(pkrel
);
11481 foreach(indexoidscan
, indexoidlist
)
11483 HeapTuple indexTuple
;
11484 Form_pg_index indexStruct
;
11486 indexoid
= lfirst_oid(indexoidscan
);
11487 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11488 if (!HeapTupleIsValid(indexTuple
))
11489 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11490 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11493 * Must have the right number of columns; must be unique and not a
11494 * partial index; forget it if there are any expressions, too. Invalid
11495 * indexes are out as well.
11497 if (indexStruct
->indnkeyatts
== numattrs
&&
11498 indexStruct
->indisunique
&&
11499 indexStruct
->indisvalid
&&
11500 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
11501 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
11503 Datum indclassDatum
;
11504 oidvector
*indclass
;
11506 /* Must get indclass the hard way */
11507 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11508 Anum_pg_index_indclass
);
11509 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11512 * The given attnum list may match the index columns in any order.
11513 * Check for a match, and extract the appropriate opclasses while
11516 * We know that attnums[] is duplicate-free per the test at the
11517 * start of this function, and we checked above that the number of
11518 * index columns agrees, so if we find a match for each attnums[]
11519 * entry then we must have a one-to-one match in some order.
11521 for (i
= 0; i
< numattrs
; i
++)
11524 for (j
= 0; j
< numattrs
; j
++)
11526 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
11528 opclasses
[i
] = indclass
->values
[j
];
11538 * Refuse to use a deferrable unique/primary key. This is per SQL
11539 * spec, and there would be a lot of interesting semantic problems
11540 * if we tried to allow it.
11542 if (found
&& !indexStruct
->indimmediate
)
11545 * Remember that we found an otherwise matching index, so that
11546 * we can generate a more appropriate error message.
11548 found_deferrable
= true;
11552 ReleaseSysCache(indexTuple
);
11559 if (found_deferrable
)
11561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11562 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
11563 RelationGetRelationName(pkrel
))));
11566 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
11567 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
11568 RelationGetRelationName(pkrel
))));
11571 list_free(indexoidlist
);
11579 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
11580 * Caller has equal regard for binary coercibility and for an exact match.
11582 static CoercionPathType
11583 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
11585 CoercionPathType ret
;
11587 if (targetTypeId
== sourceTypeId
)
11589 ret
= COERCION_PATH_RELABELTYPE
;
11590 *funcid
= InvalidOid
;
11594 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
11595 COERCION_IMPLICIT
, funcid
);
11596 if (ret
== COERCION_PATH_NONE
)
11597 /* A previously-relied-upon cast is now gone. */
11598 elog(ERROR
, "could not find cast from %u to %u",
11599 sourceTypeId
, targetTypeId
);
11606 * Permissions checks on the referenced table for ADD FOREIGN KEY
11608 * Note: we have already checked that the user owns the referencing table,
11609 * else we'd have failed much earlier; no additional checks are needed for it.
11612 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
11614 Oid roleid
= GetUserId();
11615 AclResult aclresult
;
11618 /* Okay if we have relation-level REFERENCES permission */
11619 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
11621 if (aclresult
== ACLCHECK_OK
)
11623 /* Else we must have REFERENCES on each column */
11624 for (i
= 0; i
< natts
; i
++)
11626 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
11627 roleid
, ACL_REFERENCES
);
11628 if (aclresult
!= ACLCHECK_OK
)
11629 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
11630 RelationGetRelationName(rel
));
11635 * Scan the existing rows in a table to verify they meet a proposed FK
11638 * Caller must have opened and locked both relations appropriately.
11641 validateForeignKeyConstraint(char *conname
,
11647 TupleTableSlot
*slot
;
11648 TableScanDesc scan
;
11649 Trigger trig
= {0};
11651 MemoryContext oldcxt
;
11652 MemoryContext perTupCxt
;
11655 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
11658 * Build a trigger call structure; we'll need it either way.
11660 trig
.tgoid
= InvalidOid
;
11661 trig
.tgname
= conname
;
11662 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
11663 trig
.tgisinternal
= true;
11664 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
11665 trig
.tgconstrindid
= pkindOid
;
11666 trig
.tgconstraint
= constraintOid
;
11667 trig
.tgdeferrable
= false;
11668 trig
.tginitdeferred
= false;
11669 /* we needn't fill in remaining fields */
11672 * See if we can do it with a single LEFT JOIN query. A false result
11673 * indicates we must proceed with the fire-the-trigger method.
11675 if (RI_Initial_Check(&trig
, rel
, pkrel
))
11679 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
11680 * if that tuple had just been inserted. If any of those fail, it should
11681 * ereport(ERROR) and that's that.
11683 snapshot
= RegisterSnapshot(GetLatestSnapshot());
11684 slot
= table_slot_create(rel
, NULL
);
11685 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
11687 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
11688 "validateForeignKeyConstraint",
11689 ALLOCSET_SMALL_SIZES
);
11690 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
11692 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
11694 LOCAL_FCINFO(fcinfo
, 0);
11695 TriggerData trigdata
= {0};
11697 CHECK_FOR_INTERRUPTS();
11700 * Make a call to the trigger function
11702 * No parameters are passed, but we do set a context
11704 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
11707 * We assume RI_FKey_check_ins won't look at flinfo...
11709 trigdata
.type
= T_TriggerData
;
11710 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
11711 trigdata
.tg_relation
= rel
;
11712 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
11713 trigdata
.tg_trigslot
= slot
;
11714 trigdata
.tg_trigger
= &trig
;
11716 fcinfo
->context
= (Node
*) &trigdata
;
11718 RI_FKey_check_ins(fcinfo
);
11720 MemoryContextReset(perTupCxt
);
11723 MemoryContextSwitchTo(oldcxt
);
11724 MemoryContextDelete(perTupCxt
);
11725 table_endscan(scan
);
11726 UnregisterSnapshot(snapshot
);
11727 ExecDropSingleTupleTableSlot(slot
);
11731 * CreateFKCheckTrigger
11732 * Creates the insert (on_insert=true) or update "check" trigger that
11733 * implements a given foreign key
11735 * Returns the OID of the so created trigger.
11738 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
11739 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
11742 ObjectAddress trigAddress
;
11743 CreateTrigStmt
*fk_trigger
;
11746 * Note: for a self-referential FK (referencing and referenced tables are
11747 * the same), it is important that the ON UPDATE action fires before the
11748 * CHECK action, since both triggers will fire on the same row during an
11749 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
11750 * state of the row. Triggers fire in name order, so we ensure this by
11751 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
11752 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
11754 fk_trigger
= makeNode(CreateTrigStmt
);
11755 fk_trigger
->replace
= false;
11756 fk_trigger
->isconstraint
= true;
11757 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
11758 fk_trigger
->relation
= NULL
;
11760 /* Either ON INSERT or ON UPDATE */
11763 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
11764 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
11768 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
11769 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
11772 fk_trigger
->args
= NIL
;
11773 fk_trigger
->row
= true;
11774 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
11775 fk_trigger
->columns
= NIL
;
11776 fk_trigger
->whenClause
= NULL
;
11777 fk_trigger
->transitionRels
= NIL
;
11778 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
11779 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
11780 fk_trigger
->constrrel
= NULL
;
11782 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
11783 constraintOid
, indexOid
, InvalidOid
,
11784 parentTrigOid
, NULL
, true, false);
11786 /* Make changes-so-far visible */
11787 CommandCounterIncrement();
11789 return trigAddress
.objectId
;
11793 * createForeignKeyActionTriggers
11794 * Create the referenced-side "action" triggers that implement a foreign
11797 * Returns the OIDs of the so created triggers in *deleteTrigOid and
11801 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
11802 Oid constraintOid
, Oid indexOid
,
11803 Oid parentDelTrigger
, Oid parentUpdTrigger
,
11804 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
11806 CreateTrigStmt
*fk_trigger
;
11807 ObjectAddress trigAddress
;
11810 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11811 * DELETE action on the referenced table.
11813 fk_trigger
= makeNode(CreateTrigStmt
);
11814 fk_trigger
->replace
= false;
11815 fk_trigger
->isconstraint
= true;
11816 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
11817 fk_trigger
->relation
= NULL
;
11818 fk_trigger
->args
= NIL
;
11819 fk_trigger
->row
= true;
11820 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
11821 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
11822 fk_trigger
->columns
= NIL
;
11823 fk_trigger
->whenClause
= NULL
;
11824 fk_trigger
->transitionRels
= NIL
;
11825 fk_trigger
->constrrel
= NULL
;
11826 switch (fkconstraint
->fk_del_action
)
11828 case FKCONSTR_ACTION_NOACTION
:
11829 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
11830 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
11831 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
11833 case FKCONSTR_ACTION_RESTRICT
:
11834 fk_trigger
->deferrable
= false;
11835 fk_trigger
->initdeferred
= false;
11836 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
11838 case FKCONSTR_ACTION_CASCADE
:
11839 fk_trigger
->deferrable
= false;
11840 fk_trigger
->initdeferred
= false;
11841 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
11843 case FKCONSTR_ACTION_SETNULL
:
11844 fk_trigger
->deferrable
= false;
11845 fk_trigger
->initdeferred
= false;
11846 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
11848 case FKCONSTR_ACTION_SETDEFAULT
:
11849 fk_trigger
->deferrable
= false;
11850 fk_trigger
->initdeferred
= false;
11851 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
11854 elog(ERROR
, "unrecognized FK action type: %d",
11855 (int) fkconstraint
->fk_del_action
);
11859 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
11860 RelationGetRelid(rel
),
11861 constraintOid
, indexOid
, InvalidOid
,
11862 parentDelTrigger
, NULL
, true, false);
11864 *deleteTrigOid
= trigAddress
.objectId
;
11866 /* Make changes-so-far visible */
11867 CommandCounterIncrement();
11870 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11871 * UPDATE action on the referenced table.
11873 fk_trigger
= makeNode(CreateTrigStmt
);
11874 fk_trigger
->replace
= false;
11875 fk_trigger
->isconstraint
= true;
11876 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
11877 fk_trigger
->relation
= NULL
;
11878 fk_trigger
->args
= NIL
;
11879 fk_trigger
->row
= true;
11880 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
11881 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
11882 fk_trigger
->columns
= NIL
;
11883 fk_trigger
->whenClause
= NULL
;
11884 fk_trigger
->transitionRels
= NIL
;
11885 fk_trigger
->constrrel
= NULL
;
11886 switch (fkconstraint
->fk_upd_action
)
11888 case FKCONSTR_ACTION_NOACTION
:
11889 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
11890 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
11891 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
11893 case FKCONSTR_ACTION_RESTRICT
:
11894 fk_trigger
->deferrable
= false;
11895 fk_trigger
->initdeferred
= false;
11896 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
11898 case FKCONSTR_ACTION_CASCADE
:
11899 fk_trigger
->deferrable
= false;
11900 fk_trigger
->initdeferred
= false;
11901 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
11903 case FKCONSTR_ACTION_SETNULL
:
11904 fk_trigger
->deferrable
= false;
11905 fk_trigger
->initdeferred
= false;
11906 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
11908 case FKCONSTR_ACTION_SETDEFAULT
:
11909 fk_trigger
->deferrable
= false;
11910 fk_trigger
->initdeferred
= false;
11911 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
11914 elog(ERROR
, "unrecognized FK action type: %d",
11915 (int) fkconstraint
->fk_upd_action
);
11919 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
11920 RelationGetRelid(rel
),
11921 constraintOid
, indexOid
, InvalidOid
,
11922 parentUpdTrigger
, NULL
, true, false);
11924 *updateTrigOid
= trigAddress
.objectId
;
11928 * createForeignKeyCheckTriggers
11929 * Create the referencing-side "check" triggers that implement a foreign
11932 * Returns the OIDs of the so created triggers in *insertTrigOid and
11936 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
11937 Constraint
*fkconstraint
, Oid constraintOid
,
11939 Oid parentInsTrigger
, Oid parentUpdTrigger
,
11940 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
11942 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
11943 constraintOid
, indexOid
,
11944 parentInsTrigger
, true);
11945 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
11946 constraintOid
, indexOid
,
11947 parentUpdTrigger
, false);
11951 * ALTER TABLE DROP CONSTRAINT
11953 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
11956 ATExecDropConstraint(Relation rel
, const char *constrName
,
11957 DropBehavior behavior
,
11958 bool recurse
, bool recursing
,
11959 bool missing_ok
, LOCKMODE lockmode
)
11964 Form_pg_constraint con
;
11966 ScanKeyData skey
[3];
11968 bool found
= false;
11969 bool is_no_inherit_constraint
= false;
11972 /* At top level, permission check was done in ATPrepCmd, else do it */
11974 ATSimplePermissions(AT_DropConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
11976 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11979 * Find and drop the target constraint
11981 ScanKeyInit(&skey
[0],
11982 Anum_pg_constraint_conrelid
,
11983 BTEqualStrategyNumber
, F_OIDEQ
,
11984 ObjectIdGetDatum(RelationGetRelid(rel
)));
11985 ScanKeyInit(&skey
[1],
11986 Anum_pg_constraint_contypid
,
11987 BTEqualStrategyNumber
, F_OIDEQ
,
11988 ObjectIdGetDatum(InvalidOid
));
11989 ScanKeyInit(&skey
[2],
11990 Anum_pg_constraint_conname
,
11991 BTEqualStrategyNumber
, F_NAMEEQ
,
11992 CStringGetDatum(constrName
));
11993 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11994 true, NULL
, 3, skey
);
11996 /* There can be at most one matching row */
11997 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
11999 ObjectAddress conobj
;
12001 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
12003 /* Don't drop inherited constraints */
12004 if (con
->coninhcount
> 0 && !recursing
)
12006 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12007 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12008 constrName
, RelationGetRelationName(rel
))));
12010 is_no_inherit_constraint
= con
->connoinherit
;
12011 contype
= con
->contype
;
12014 * If it's a foreign-key constraint, we'd better lock the referenced
12015 * table and check that that's not in use, just as we've already done
12016 * for the constrained table (else we might, eg, be dropping a trigger
12017 * that has unfired events). But we can/must skip that in the
12018 * self-referential case.
12020 if (contype
== CONSTRAINT_FOREIGN
&&
12021 con
->confrelid
!= RelationGetRelid(rel
))
12025 /* Must match lock taken by RemoveTriggerById: */
12026 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
12027 CheckTableNotInUse(frel
, "ALTER TABLE");
12028 table_close(frel
, NoLock
);
12032 * Perform the actual constraint deletion
12034 conobj
.classId
= ConstraintRelationId
;
12035 conobj
.objectId
= con
->oid
;
12036 conobj
.objectSubId
= 0;
12038 performDeletion(&conobj
, behavior
, 0);
12043 systable_endscan(scan
);
12050 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12051 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12052 constrName
, RelationGetRelationName(rel
))));
12057 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12058 constrName
, RelationGetRelationName(rel
))));
12059 table_close(conrel
, RowExclusiveLock
);
12065 * For partitioned tables, non-CHECK inherited constraints are dropped via
12066 * the dependency mechanism, so we're done here.
12068 if (contype
!= CONSTRAINT_CHECK
&&
12069 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
12071 table_close(conrel
, RowExclusiveLock
);
12076 * Propagate to children as appropriate. Unlike most other ALTER
12077 * routines, we have to do this one level of recursion at a time; we can't
12078 * use find_all_inheritors to do it in one pass.
12080 if (!is_no_inherit_constraint
)
12081 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
12086 * For a partitioned table, if partitions exist and we are told not to
12087 * recurse, it's a user error. It doesn't make sense to have a constraint
12088 * be defined only on the parent, especially if it's a partitioned table.
12090 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
12091 children
!= NIL
&& !recurse
)
12093 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12094 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12095 errhint("Do not specify the ONLY keyword.")));
12097 foreach(child
, children
)
12099 Oid childrelid
= lfirst_oid(child
);
12101 HeapTuple copy_tuple
;
12103 /* find_inheritance_children already got lock */
12104 childrel
= table_open(childrelid
, NoLock
);
12105 CheckTableNotInUse(childrel
, "ALTER TABLE");
12107 ScanKeyInit(&skey
[0],
12108 Anum_pg_constraint_conrelid
,
12109 BTEqualStrategyNumber
, F_OIDEQ
,
12110 ObjectIdGetDatum(childrelid
));
12111 ScanKeyInit(&skey
[1],
12112 Anum_pg_constraint_contypid
,
12113 BTEqualStrategyNumber
, F_OIDEQ
,
12114 ObjectIdGetDatum(InvalidOid
));
12115 ScanKeyInit(&skey
[2],
12116 Anum_pg_constraint_conname
,
12117 BTEqualStrategyNumber
, F_NAMEEQ
,
12118 CStringGetDatum(constrName
));
12119 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12120 true, NULL
, 3, skey
);
12122 /* There can be at most one matching row */
12123 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12125 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12126 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12128 RelationGetRelationName(childrel
))));
12130 copy_tuple
= heap_copytuple(tuple
);
12132 systable_endscan(scan
);
12134 con
= (Form_pg_constraint
) GETSTRUCT(copy_tuple
);
12136 /* Right now only CHECK constraints can be inherited */
12137 if (con
->contype
!= CONSTRAINT_CHECK
)
12138 elog(ERROR
, "inherited constraint is not a CHECK constraint");
12140 if (con
->coninhcount
<= 0) /* shouldn't happen */
12141 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
12142 childrelid
, constrName
);
12147 * If the child constraint has other definition sources, just
12148 * decrement its inheritance count; if not, recurse to delete it.
12150 if (con
->coninhcount
== 1 && !con
->conislocal
)
12152 /* Time to delete this child constraint, too */
12153 ATExecDropConstraint(childrel
, constrName
, behavior
,
12159 /* Child constraint must survive my deletion */
12160 con
->coninhcount
--;
12161 CatalogTupleUpdate(conrel
, ©_tuple
->t_self
, copy_tuple
);
12163 /* Make update visible */
12164 CommandCounterIncrement();
12170 * If we were told to drop ONLY in this table (no recursion), we
12171 * need to mark the inheritors' constraints as locally defined
12172 * rather than inherited.
12174 con
->coninhcount
--;
12175 con
->conislocal
= true;
12177 CatalogTupleUpdate(conrel
, ©_tuple
->t_self
, copy_tuple
);
12179 /* Make update visible */
12180 CommandCounterIncrement();
12183 heap_freetuple(copy_tuple
);
12185 table_close(childrel
, NoLock
);
12188 table_close(conrel
, RowExclusiveLock
);
12192 * ALTER COLUMN TYPE
12194 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12195 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12196 * transformed (and must be, because we rely on some transformed fields).
12198 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12199 * table will be done "in parallel" during phase 3, so all the USING
12200 * expressions should be parsed assuming the original column types. Also,
12201 * this allows a USING expression to refer to a field that will be dropped.
12203 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12204 * the first two execution steps in phase 2; they must not see the effects
12205 * of any other subcommand types, since the USING expressions are parsed
12206 * against the unmodified table's state.
12209 ATPrepAlterColumnType(List
**wqueue
,
12210 AlteredTableInfo
*tab
, Relation rel
,
12211 bool recurse
, bool recursing
,
12212 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
12213 AlterTableUtilityContext
*context
)
12215 char *colName
= cmd
->name
;
12216 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
12217 TypeName
*typeName
= def
->typeName
;
12218 Node
*transform
= def
->cooked_default
;
12220 Form_pg_attribute attTup
;
12223 int32 targettypmod
;
12225 NewColumnValue
*newval
;
12226 ParseState
*pstate
= make_parsestate(NULL
);
12227 AclResult aclresult
;
12230 if (rel
->rd_rel
->reloftype
&& !recursing
)
12232 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12233 errmsg("cannot alter column type of typed table")));
12235 /* lookup the attribute so we can check inheritance status */
12236 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
12237 if (!HeapTupleIsValid(tuple
))
12239 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12240 errmsg("column \"%s\" of relation \"%s\" does not exist",
12241 colName
, RelationGetRelationName(rel
))));
12242 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
12243 attnum
= attTup
->attnum
;
12245 /* Can't alter a system attribute */
12248 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12249 errmsg("cannot alter system column \"%s\"",
12253 * Don't alter inherited columns. At outer level, there had better not be
12254 * any inherited definition; when recursing, we assume this was checked at
12255 * the parent level (see below).
12257 if (attTup
->attinhcount
> 0 && !recursing
)
12259 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12260 errmsg("cannot alter inherited column \"%s\"",
12263 /* Don't alter columns used in the partition key */
12264 if (has_partition_attrs(rel
,
12265 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
12268 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12269 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12270 colName
, RelationGetRelationName(rel
))));
12272 /* Look up the target type */
12273 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
12275 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
12276 if (aclresult
!= ACLCHECK_OK
)
12277 aclcheck_error_type(aclresult
, targettype
);
12279 /* And the collation */
12280 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
12282 /* make sure datatype is legal for a column */
12283 CheckAttributeType(colName
, targettype
, targetcollid
,
12284 list_make1_oid(rel
->rd_rel
->reltype
),
12287 if (tab
->relkind
== RELKIND_RELATION
||
12288 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
12291 * Set up an expression to transform the old data value to the new
12292 * type. If a USING option was given, use the expression as
12293 * transformed by transformAlterTableStmt, else just take the old
12294 * value and try to coerce it. We do this first so that type
12295 * incompatibility can be detected before we waste effort, and because
12296 * we need the expression to be parsed against the original table row
12301 transform
= (Node
*) makeVar(1, attnum
,
12302 attTup
->atttypid
, attTup
->atttypmod
,
12303 attTup
->attcollation
,
12307 transform
= coerce_to_target_type(pstate
,
12308 transform
, exprType(transform
),
12309 targettype
, targettypmod
,
12310 COERCION_ASSIGNMENT
,
12311 COERCE_IMPLICIT_CAST
,
12313 if (transform
== NULL
)
12315 /* error text depends on whether USING was specified or not */
12316 if (def
->cooked_default
!= NULL
)
12318 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12319 errmsg("result of USING clause for column \"%s\""
12320 " cannot be cast automatically to type %s",
12321 colName
, format_type_be(targettype
)),
12322 errhint("You might need to add an explicit cast.")));
12325 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12326 errmsg("column \"%s\" cannot be cast automatically to type %s",
12327 colName
, format_type_be(targettype
)),
12328 /* translator: USING is SQL, don't translate it */
12329 errhint("You might need to specify \"USING %s::%s\".",
12330 quote_identifier(colName
),
12331 format_type_with_typemod(targettype
,
12335 /* Fix collations after all else */
12336 assign_expr_collations(pstate
, transform
);
12338 /* Plan the expr now so we can accurately assess the need to rewrite. */
12339 transform
= (Node
*) expression_planner((Expr
*) transform
);
12342 * Add a work queue item to make ATRewriteTable update the column
12345 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
12346 newval
->attnum
= attnum
;
12347 newval
->expr
= (Expr
*) transform
;
12348 newval
->is_generated
= false;
12350 tab
->newvals
= lappend(tab
->newvals
, newval
);
12351 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
12352 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
12354 else if (transform
)
12356 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12357 errmsg("\"%s\" is not a table",
12358 RelationGetRelationName(rel
))));
12360 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
12363 * For relations without storage, do this check now. Regular tables
12364 * will check it later when the table is being rewritten.
12366 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
12369 ReleaseSysCache(tuple
);
12372 * Recurse manually by queueing a new command for each child, if
12373 * necessary. We cannot apply ATSimpleRecursion here because we need to
12374 * remap attribute numbers in the USING expression, if any.
12376 * If we are told not to recurse, there had better not be any child
12377 * tables; else the alter would put them out of step.
12381 Oid relid
= RelationGetRelid(rel
);
12387 child_oids
= find_all_inheritors(relid
, lockmode
,
12388 &child_numparents
);
12391 * find_all_inheritors does the recursive search of the inheritance
12392 * hierarchy, so all we have to do is process all of the relids in the
12393 * list that it returns.
12395 forboth(lo
, child_oids
, li
, child_numparents
)
12397 Oid childrelid
= lfirst_oid(lo
);
12398 int numparents
= lfirst_int(li
);
12400 HeapTuple childtuple
;
12401 Form_pg_attribute childattTup
;
12403 if (childrelid
== relid
)
12406 /* find_all_inheritors already got lock */
12407 childrel
= relation_open(childrelid
, NoLock
);
12408 CheckTableNotInUse(childrel
, "ALTER TABLE");
12411 * Verify that the child doesn't have any inherited definitions of
12412 * this column that came from outside this inheritance hierarchy.
12413 * (renameatt makes a similar test, though in a different way
12414 * because of its different recursion mechanism.)
12416 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
12418 if (!HeapTupleIsValid(childtuple
))
12420 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12421 errmsg("column \"%s\" of relation \"%s\" does not exist",
12422 colName
, RelationGetRelationName(childrel
))));
12423 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
12425 if (childattTup
->attinhcount
> numparents
)
12427 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12428 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12429 colName
, RelationGetRelationName(childrel
))));
12431 ReleaseSysCache(childtuple
);
12434 * Remap the attribute numbers. If no USING expression was
12435 * specified, there is no need for this step.
12437 if (def
->cooked_default
)
12440 bool found_whole_row
;
12442 /* create a copy to scribble on */
12443 cmd
= copyObject(cmd
);
12445 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
12446 RelationGetDescr(rel
),
12448 ((ColumnDef
*) cmd
->def
)->cooked_default
=
12449 map_variable_attnos(def
->cooked_default
,
12452 InvalidOid
, &found_whole_row
);
12453 if (found_whole_row
)
12455 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12456 errmsg("cannot convert whole-row table reference"),
12457 errdetail("USING expression contains a whole-row table reference.")));
12460 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
12461 relation_close(childrel
, NoLock
);
12464 else if (!recursing
&&
12465 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
12467 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12468 errmsg("type of inherited column \"%s\" must be changed in child tables too",
12471 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
12472 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
12476 * When the data type of a column is changed, a rewrite might not be required
12477 * if the new type is sufficiently identical to the old one, and the USING
12478 * clause isn't trying to insert some other value. It's safe to skip the
12479 * rewrite in these cases:
12481 * - the old type is binary coercible to the new type
12482 * - the new type is an unconstrained domain over the old type
12483 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
12485 * In the case of a constrained domain, we could get by with scanning the
12486 * table and checking the constraint rather than actually rewriting it, but we
12487 * don't currently try to do that.
12490 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
12492 Assert(expr
!= NULL
);
12496 /* only one varno, so no need to check that */
12497 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
12499 else if (IsA(expr
, RelabelType
))
12500 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
12501 else if (IsA(expr
, CoerceToDomain
))
12503 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
12505 if (DomainHasConstraints(d
->resulttype
))
12507 expr
= (Node
*) d
->arg
;
12509 else if (IsA(expr
, FuncExpr
))
12511 FuncExpr
*f
= (FuncExpr
*) expr
;
12515 case F_TIMESTAMPTZ_TIMESTAMP
:
12516 case F_TIMESTAMP_TIMESTAMPTZ
:
12517 if (TimestampTimestampTzRequiresRewrite())
12520 expr
= linitial(f
->args
);
12532 * ALTER COLUMN .. SET DATA TYPE
12534 * Return the address of the modified column.
12536 static ObjectAddress
12537 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
12538 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
12540 char *colName
= cmd
->name
;
12541 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
12542 TypeName
*typeName
= def
->typeName
;
12544 Form_pg_attribute attTup
,
12547 HeapTuple typeTuple
;
12548 Form_pg_type tform
;
12550 int32 targettypmod
;
12553 Relation attrelation
;
12555 ScanKeyData key
[3];
12558 ObjectAddress address
;
12561 * Clear all the missing values if we're rewriting the table, since this
12562 * renders them pointless.
12568 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
12569 RelationClearMissing(newrel
);
12570 relation_close(newrel
, NoLock
);
12571 /* make sure we don't conflict with later attribute modifications */
12572 CommandCounterIncrement();
12575 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
12577 /* Look up the target column */
12578 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
12579 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
12581 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12582 errmsg("column \"%s\" of relation \"%s\" does not exist",
12583 colName
, RelationGetRelationName(rel
))));
12584 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
12585 attnum
= attTup
->attnum
;
12586 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
12588 /* Check for multiple ALTER TYPE on same column --- can't cope */
12589 if (attTup
->atttypid
!= attOldTup
->atttypid
||
12590 attTup
->atttypmod
!= attOldTup
->atttypmod
)
12592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12593 errmsg("cannot alter type of column \"%s\" twice",
12596 /* Look up the target type (should not fail, since prep found it) */
12597 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
12598 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
12599 targettype
= tform
->oid
;
12600 /* And the collation */
12601 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
12604 * If there is a default expression for the column, get it and ensure we
12605 * can coerce it to the new datatype. (We must do this before changing
12606 * the column type, because build_column_default itself will try to
12607 * coerce, and will not issue the error message we want if it fails.)
12609 * We remove any implicit coercion steps at the top level of the old
12610 * default expression; this has been agreed to satisfy the principle of
12611 * least surprise. (The conversion to the new column type should act like
12612 * it started from what the user sees as the stored expression, and the
12613 * implicit coercions aren't going to be shown.)
12615 if (attTup
->atthasdef
)
12617 defaultexpr
= build_column_default(rel
, attnum
);
12618 Assert(defaultexpr
);
12619 defaultexpr
= strip_implicit_coercions(defaultexpr
);
12620 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
12621 defaultexpr
, exprType(defaultexpr
),
12622 targettype
, targettypmod
,
12623 COERCION_ASSIGNMENT
,
12624 COERCE_IMPLICIT_CAST
,
12626 if (defaultexpr
== NULL
)
12628 if (attTup
->attgenerated
)
12630 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12631 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
12632 colName
, format_type_be(targettype
))));
12635 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12636 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
12637 colName
, format_type_be(targettype
))));
12641 defaultexpr
= NULL
;
12644 * Find everything that depends on the column (constraints, indexes, etc),
12645 * and record enough information to let us recreate the objects.
12647 * The actual recreation does not happen here, but only after we have
12648 * performed all the individual ALTER TYPE operations. We have to save
12649 * the info before executing ALTER TYPE, though, else the deparser will
12652 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
12654 ScanKeyInit(&key
[0],
12655 Anum_pg_depend_refclassid
,
12656 BTEqualStrategyNumber
, F_OIDEQ
,
12657 ObjectIdGetDatum(RelationRelationId
));
12658 ScanKeyInit(&key
[1],
12659 Anum_pg_depend_refobjid
,
12660 BTEqualStrategyNumber
, F_OIDEQ
,
12661 ObjectIdGetDatum(RelationGetRelid(rel
)));
12662 ScanKeyInit(&key
[2],
12663 Anum_pg_depend_refobjsubid
,
12664 BTEqualStrategyNumber
, F_INT4EQ
,
12665 Int32GetDatum((int32
) attnum
));
12667 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
12670 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
12672 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
12673 ObjectAddress foundObject
;
12675 foundObject
.classId
= foundDep
->classid
;
12676 foundObject
.objectId
= foundDep
->objid
;
12677 foundObject
.objectSubId
= foundDep
->objsubid
;
12679 switch (getObjectClass(&foundObject
))
12683 char relKind
= get_rel_relkind(foundObject
.objectId
);
12685 if (relKind
== RELKIND_INDEX
||
12686 relKind
== RELKIND_PARTITIONED_INDEX
)
12688 Assert(foundObject
.objectSubId
== 0);
12689 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
12691 else if (relKind
== RELKIND_SEQUENCE
)
12694 * This must be a SERIAL column's sequence. We need
12695 * not do anything to it.
12697 Assert(foundObject
.objectSubId
== 0);
12701 /* Not expecting any other direct dependencies... */
12702 elog(ERROR
, "unexpected object depending on column: %s",
12703 getObjectDescription(&foundObject
, false));
12708 case OCLASS_CONSTRAINT
:
12709 Assert(foundObject
.objectSubId
== 0);
12710 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
12713 case OCLASS_REWRITE
:
12714 /* XXX someday see if we can cope with revising views */
12716 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12717 errmsg("cannot alter type of a column used by a view or rule"),
12718 errdetail("%s depends on column \"%s\"",
12719 getObjectDescription(&foundObject
, false),
12723 case OCLASS_TRIGGER
:
12726 * A trigger can depend on a column because the column is
12727 * specified as an update target, or because the column is
12728 * used in the trigger's WHEN condition. The first case would
12729 * not require any extra work, but the second case would
12730 * require updating the WHEN expression, which will take a
12731 * significant amount of new code. Since we can't easily tell
12732 * which case applies, we punt for both. FIXME someday.
12735 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12736 errmsg("cannot alter type of a column used in a trigger definition"),
12737 errdetail("%s depends on column \"%s\"",
12738 getObjectDescription(&foundObject
, false),
12742 case OCLASS_POLICY
:
12745 * A policy can depend on a column because the column is
12746 * specified in the policy's USING or WITH CHECK qual
12747 * expressions. It might be possible to rewrite and recheck
12748 * the policy expression, but punt for now. It's certainly
12749 * easy enough to remove and recreate the policy; still, FIXME
12753 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12754 errmsg("cannot alter type of a column used in a policy definition"),
12755 errdetail("%s depends on column \"%s\"",
12756 getObjectDescription(&foundObject
, false),
12760 case OCLASS_DEFAULT
:
12762 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
12764 if (col
.objectId
== RelationGetRelid(rel
) &&
12765 col
.objectSubId
== attnum
)
12768 * Ignore the column's own default expression, which
12769 * we will deal with below.
12771 Assert(defaultexpr
);
12776 * This must be a reference from the expression of a
12777 * generated column elsewhere in the same table.
12778 * Changing the type of a column that is used by a
12779 * generated column is not allowed by SQL standard, so
12780 * just punt for now. It might be doable with some
12781 * thinking and effort.
12784 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12785 errmsg("cannot alter type of a column used by a generated column"),
12786 errdetail("Column \"%s\" is used by generated column \"%s\".",
12788 get_attname(col
.objectId
,
12795 case OCLASS_STATISTIC_EXT
:
12798 * Give the extended-stats machinery a chance to fix anything
12799 * that this column type change would break.
12801 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
12807 case OCLASS_COLLATION
:
12808 case OCLASS_CONVERSION
:
12809 case OCLASS_LANGUAGE
:
12810 case OCLASS_LARGEOBJECT
:
12811 case OCLASS_OPERATOR
:
12812 case OCLASS_OPCLASS
:
12813 case OCLASS_OPFAMILY
:
12816 case OCLASS_AMPROC
:
12817 case OCLASS_SCHEMA
:
12818 case OCLASS_TSPARSER
:
12819 case OCLASS_TSDICT
:
12820 case OCLASS_TSTEMPLATE
:
12821 case OCLASS_TSCONFIG
:
12823 case OCLASS_ROLE_MEMBERSHIP
:
12824 case OCLASS_DATABASE
:
12825 case OCLASS_TBLSPACE
:
12827 case OCLASS_FOREIGN_SERVER
:
12828 case OCLASS_USER_MAPPING
:
12829 case OCLASS_DEFACL
:
12830 case OCLASS_EXTENSION
:
12831 case OCLASS_EVENT_TRIGGER
:
12832 case OCLASS_PARAMETER_ACL
:
12833 case OCLASS_PUBLICATION
:
12834 case OCLASS_PUBLICATION_NAMESPACE
:
12835 case OCLASS_PUBLICATION_REL
:
12836 case OCLASS_SUBSCRIPTION
:
12837 case OCLASS_TRANSFORM
:
12840 * We don't expect any of these sorts of objects to depend on
12843 elog(ERROR
, "unexpected object depending on column: %s",
12844 getObjectDescription(&foundObject
, false));
12848 * There's intentionally no default: case here; we want the
12849 * compiler to warn if a new OCLASS hasn't been handled above.
12854 systable_endscan(scan
);
12857 * Now scan for dependencies of this column on other things. The only
12858 * things we should find are the dependency on the column datatype and
12859 * possibly a collation dependency. Those can be removed.
12861 ScanKeyInit(&key
[0],
12862 Anum_pg_depend_classid
,
12863 BTEqualStrategyNumber
, F_OIDEQ
,
12864 ObjectIdGetDatum(RelationRelationId
));
12865 ScanKeyInit(&key
[1],
12866 Anum_pg_depend_objid
,
12867 BTEqualStrategyNumber
, F_OIDEQ
,
12868 ObjectIdGetDatum(RelationGetRelid(rel
)));
12869 ScanKeyInit(&key
[2],
12870 Anum_pg_depend_objsubid
,
12871 BTEqualStrategyNumber
, F_INT4EQ
,
12872 Int32GetDatum((int32
) attnum
));
12874 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
12877 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
12879 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
12880 ObjectAddress foundObject
;
12882 foundObject
.classId
= foundDep
->refclassid
;
12883 foundObject
.objectId
= foundDep
->refobjid
;
12884 foundObject
.objectSubId
= foundDep
->refobjsubid
;
12886 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
12887 elog(ERROR
, "found unexpected dependency type '%c'",
12888 foundDep
->deptype
);
12889 if (!(foundDep
->refclassid
== TypeRelationId
&&
12890 foundDep
->refobjid
== attTup
->atttypid
) &&
12891 !(foundDep
->refclassid
== CollationRelationId
&&
12892 foundDep
->refobjid
== attTup
->attcollation
))
12893 elog(ERROR
, "found unexpected dependency for column: %s",
12894 getObjectDescription(&foundObject
, false));
12896 CatalogTupleDelete(depRel
, &depTup
->t_self
);
12899 systable_endscan(scan
);
12901 table_close(depRel
, RowExclusiveLock
);
12904 * Here we go --- change the recorded column type and collation. (Note
12905 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
12906 * fix up the missing value if any.
12908 if (attTup
->atthasmissing
)
12913 /* if rewrite is true the missing value should already be cleared */
12914 Assert(tab
->rewrite
== 0);
12916 /* Get the missing value datum */
12917 missingval
= heap_getattr(heapTup
,
12918 Anum_pg_attribute_attmissingval
,
12919 attrelation
->rd_att
,
12922 /* if it's a null array there is nothing to do */
12927 * Get the datum out of the array and repack it in a new array
12928 * built with the new type data. We assume that since the table
12929 * doesn't need rewriting, the actual Datum doesn't need to be
12930 * changed, only the array metadata.
12935 Datum valuesAtt
[Natts_pg_attribute
] = {0};
12936 bool nullsAtt
[Natts_pg_attribute
] = {0};
12937 bool replacesAtt
[Natts_pg_attribute
] = {0};
12940 missingval
= array_get_element(missingval
,
12948 missingval
= PointerGetDatum(construct_array(&missingval
,
12955 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
12956 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
12957 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
12959 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
12960 valuesAtt
, nullsAtt
, replacesAtt
);
12961 heap_freetuple(heapTup
);
12963 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
12967 attTup
->atttypid
= targettype
;
12968 attTup
->atttypmod
= targettypmod
;
12969 attTup
->attcollation
= targetcollid
;
12970 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
12972 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
12973 errmsg("too many array dimensions"));
12974 attTup
->attndims
= list_length(typeName
->arrayBounds
);
12975 attTup
->attlen
= tform
->typlen
;
12976 attTup
->attbyval
= tform
->typbyval
;
12977 attTup
->attalign
= tform
->typalign
;
12978 attTup
->attstorage
= tform
->typstorage
;
12979 attTup
->attcompression
= InvalidCompressionMethod
;
12981 ReleaseSysCache(typeTuple
);
12983 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
12985 table_close(attrelation
, RowExclusiveLock
);
12987 /* Install dependencies on new datatype and collation */
12988 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
12989 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
12992 * Drop any pg_statistic entry for the column, since it's now wrong type
12994 RemoveStatistics(RelationGetRelid(rel
), attnum
);
12996 InvokeObjectPostAlterHook(RelationRelationId
,
12997 RelationGetRelid(rel
), attnum
);
13000 * Update the default, if present, by brute force --- remove and re-add
13001 * the default. Probably unsafe to take shortcuts, since the new version
13002 * may well have additional dependencies. (It's okay to do this now,
13003 * rather than after other ALTER TYPE commands, since the default won't
13004 * depend on other column types.)
13009 * If it's a GENERATED default, drop its dependency records, in
13010 * particular its INTERNAL dependency on the column, which would
13011 * otherwise cause dependency.c to refuse to perform the deletion.
13013 if (attTup
->attgenerated
)
13015 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
13017 if (!OidIsValid(attrdefoid
))
13018 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
13019 RelationGetRelid(rel
), attnum
);
13020 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
13024 * Make updates-so-far visible, particularly the new pg_attribute row
13025 * which will be updated again.
13027 CommandCounterIncrement();
13030 * We use RESTRICT here for safety, but at present we do not expect
13031 * anything to depend on the default.
13033 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
13036 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
13039 ObjectAddressSubSet(address
, RelationRelationId
,
13040 RelationGetRelid(rel
), attnum
);
13043 heap_freetuple(heapTup
);
13049 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13050 * needs to be reset.
13053 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13055 if (!get_index_isreplident(indoid
))
13058 if (tab
->replicaIdentityIndex
)
13059 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
13061 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
13065 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13068 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13070 if (!get_index_isclustered(indoid
))
13073 if (tab
->clusterOnIndex
)
13074 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
13076 tab
->clusterOnIndex
= get_rel_name(indoid
);
13080 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13081 * to be rebuilt (which we might already know).
13084 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
13087 * This de-duplication check is critical for two independent reasons: we
13088 * mustn't try to recreate the same constraint twice, and if a constraint
13089 * depends on more than one column whose type is to be altered, we must
13090 * capture its definition string before applying any of the column type
13091 * changes. ruleutils.c will get confused if we ask again later.
13093 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
13095 /* OK, capture the constraint's existing definition string */
13096 char *defstring
= pg_get_constraintdef_command(conoid
);
13099 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
13101 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
13105 * For the index of a constraint, if any, remember if it is used for
13106 * the table's replica identity or if it is a clustered index, so that
13107 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13108 * those properties.
13110 indoid
= get_constraint_index(conoid
);
13111 if (OidIsValid(indoid
))
13113 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13114 RememberClusterOnForRebuilding(indoid
, tab
);
13120 * Subroutine for ATExecAlterColumnType: remember that an index needs
13121 * to be rebuilt (which we might already know).
13124 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13127 * This de-duplication check is critical for two independent reasons: we
13128 * mustn't try to recreate the same index twice, and if an index depends
13129 * on more than one column whose type is to be altered, we must capture
13130 * its definition string before applying any of the column type changes.
13131 * ruleutils.c will get confused if we ask again later.
13133 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
13136 * Before adding it as an index-to-rebuild, we'd better see if it
13137 * belongs to a constraint, and if so rebuild the constraint instead.
13138 * Typically this check fails, because constraint indexes normally
13139 * have only dependencies on their constraint. But it's possible for
13140 * such an index to also have direct dependencies on table columns,
13141 * for example with a partial exclusion constraint.
13143 Oid conoid
= get_index_constraint(indoid
);
13145 if (OidIsValid(conoid
))
13147 RememberConstraintForRebuilding(conoid
, tab
);
13151 /* OK, capture the index's existing definition string */
13152 char *defstring
= pg_get_indexdef_string(indoid
);
13154 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
13156 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
13160 * Remember if this index is used for the table's replica identity
13161 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13162 * can queue up commands necessary to restore those properties.
13164 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13165 RememberClusterOnForRebuilding(indoid
, tab
);
13171 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13172 * needs to be rebuilt (which we might already know).
13175 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
13178 * This de-duplication check is critical for two independent reasons: we
13179 * mustn't try to recreate the same statistics object twice, and if the
13180 * statistics object depends on more than one column whose type is to be
13181 * altered, we must capture its definition string before applying any of
13182 * the type changes. ruleutils.c will get confused if we ask again later.
13184 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
13186 /* OK, capture the statistics object's existing definition string */
13187 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
13189 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
13191 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
13197 * Cleanup after we've finished all the ALTER TYPE operations for a
13198 * particular relation. We have to drop and recreate all the indexes
13199 * and constraints that depend on the altered columns. We do the
13200 * actual dropping here, but re-creation is managed by adding work
13201 * queue entries to do those steps later.
13204 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
13207 ObjectAddresses
*objects
;
13208 ListCell
*def_item
;
13209 ListCell
*oid_item
;
13212 * Collect all the constraints and indexes to drop so we can process them
13213 * in a single call. That way we don't have to worry about dependencies
13216 objects
= new_object_addresses();
13219 * Re-parse the index and constraint definitions, and attach them to the
13220 * appropriate work queue entries. We do this before dropping because in
13221 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13222 * lock on the table the constraint is attached to, and we need to get
13223 * that before reparsing/dropping.
13225 * We can't rely on the output of deparsing to tell us which relation to
13226 * operate on, because concurrent activity might have made the name
13227 * resolve differently. Instead, we've got to use the OID of the
13228 * constraint or index we're processing to figure out which relation to
13231 forboth(oid_item
, tab
->changedConstraintOids
,
13232 def_item
, tab
->changedConstraintDefs
)
13234 Oid oldId
= lfirst_oid(oid_item
);
13236 Form_pg_constraint con
;
13242 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
13243 if (!HeapTupleIsValid(tup
)) /* should not happen */
13244 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
13245 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
13246 if (OidIsValid(con
->conrelid
))
13247 relid
= con
->conrelid
;
13250 /* must be a domain constraint */
13251 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
13252 if (!OidIsValid(relid
))
13253 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
13255 confrelid
= con
->confrelid
;
13256 contype
= con
->contype
;
13257 conislocal
= con
->conislocal
;
13258 ReleaseSysCache(tup
);
13260 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
13261 add_exact_object_address(&obj
, objects
);
13264 * If the constraint is inherited (only), we don't want to inject a
13265 * new definition here; it'll get recreated when ATAddCheckConstraint
13266 * recurses from adding the parent table's constraint. But we had to
13267 * carry the info this far so that we can drop the constraint below.
13273 * When rebuilding an FK constraint that references the table we're
13274 * modifying, we might not yet have any lock on the FK's table, so get
13275 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13276 * step, so there's no value in asking for anything weaker.
13278 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
13279 LockRelationOid(relid
, AccessExclusiveLock
);
13281 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
13282 (char *) lfirst(def_item
),
13283 wqueue
, lockmode
, tab
->rewrite
);
13285 forboth(oid_item
, tab
->changedIndexOids
,
13286 def_item
, tab
->changedIndexDefs
)
13288 Oid oldId
= lfirst_oid(oid_item
);
13291 relid
= IndexGetRelation(oldId
, false);
13292 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13293 (char *) lfirst(def_item
),
13294 wqueue
, lockmode
, tab
->rewrite
);
13296 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
13297 add_exact_object_address(&obj
, objects
);
13300 /* add dependencies for new statistics */
13301 forboth(oid_item
, tab
->changedStatisticsOids
,
13302 def_item
, tab
->changedStatisticsDefs
)
13304 Oid oldId
= lfirst_oid(oid_item
);
13307 relid
= StatisticsGetRelation(oldId
, false);
13308 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13309 (char *) lfirst(def_item
),
13310 wqueue
, lockmode
, tab
->rewrite
);
13312 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
13313 add_exact_object_address(&obj
, objects
);
13317 * Queue up command to restore replica identity index marking
13319 if (tab
->replicaIdentityIndex
)
13321 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13322 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
13324 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
13325 subcmd
->name
= tab
->replicaIdentityIndex
;
13326 cmd
->subtype
= AT_ReplicaIdentity
;
13327 cmd
->def
= (Node
*) subcmd
;
13329 /* do it after indexes and constraints */
13330 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13331 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13335 * Queue up command to restore marking of index used for cluster.
13337 if (tab
->clusterOnIndex
)
13339 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13341 cmd
->subtype
= AT_ClusterOn
;
13342 cmd
->name
= tab
->clusterOnIndex
;
13344 /* do it after indexes and constraints */
13345 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13346 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13350 * It should be okay to use DROP_RESTRICT here, since nothing else should
13351 * be depending on these objects.
13353 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
13355 free_object_addresses(objects
);
13358 * The objects will get recreated during subsequent passes over the work
13364 * Parse the previously-saved definition string for a constraint, index or
13365 * statistics object against the newly-established column data type(s), and
13366 * queue up the resulting command parsetrees for execution.
13368 * This might fail if, for example, you have a WHERE clause that uses an
13369 * operator that's not available for the new column type.
13372 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
13373 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
13375 List
*raw_parsetree_list
;
13376 List
*querytree_list
;
13377 ListCell
*list_item
;
13381 * We expect that we will get only ALTER TABLE and CREATE INDEX
13382 * statements. Hence, there is no need to pass them through
13383 * parse_analyze_*() or the rewriter, but instead we need to pass them
13384 * through parse_utilcmd.c to make them ready for execution.
13386 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
13387 querytree_list
= NIL
;
13388 foreach(list_item
, raw_parsetree_list
)
13390 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
13391 Node
*stmt
= rs
->stmt
;
13393 if (IsA(stmt
, IndexStmt
))
13394 querytree_list
= lappend(querytree_list
,
13395 transformIndexStmt(oldRelId
,
13396 (IndexStmt
*) stmt
,
13398 else if (IsA(stmt
, AlterTableStmt
))
13403 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
13404 (AlterTableStmt
*) stmt
,
13408 querytree_list
= list_concat(querytree_list
, beforeStmts
);
13409 querytree_list
= lappend(querytree_list
, stmt
);
13410 querytree_list
= list_concat(querytree_list
, afterStmts
);
13412 else if (IsA(stmt
, CreateStatsStmt
))
13413 querytree_list
= lappend(querytree_list
,
13414 transformStatsStmt(oldRelId
,
13415 (CreateStatsStmt
*) stmt
,
13418 querytree_list
= lappend(querytree_list
, stmt
);
13421 /* Caller should already have acquired whatever lock we need. */
13422 rel
= relation_open(oldRelId
, NoLock
);
13425 * Attach each generated command to the proper place in the work queue.
13426 * Note this could result in creation of entirely new work-queue entries.
13428 * Also note that we have to tweak the command subtypes, because it turns
13429 * out that re-creation of indexes and constraints has to act a bit
13430 * differently from initial creation.
13432 foreach(list_item
, querytree_list
)
13434 Node
*stm
= (Node
*) lfirst(list_item
);
13435 AlteredTableInfo
*tab
;
13437 tab
= ATGetQueueEntry(wqueue
, rel
);
13439 if (IsA(stm
, IndexStmt
))
13441 IndexStmt
*stmt
= (IndexStmt
*) stm
;
13442 AlterTableCmd
*newcmd
;
13445 TryReuseIndex(oldId
, stmt
);
13446 stmt
->reset_default_tblspc
= true;
13447 /* keep the index's comment */
13448 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
13450 newcmd
= makeNode(AlterTableCmd
);
13451 newcmd
->subtype
= AT_ReAddIndex
;
13452 newcmd
->def
= (Node
*) stmt
;
13453 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
13454 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
13456 else if (IsA(stm
, AlterTableStmt
))
13458 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
13461 foreach(lcmd
, stmt
->cmds
)
13463 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
13465 if (cmd
->subtype
== AT_AddIndex
)
13467 IndexStmt
*indstmt
;
13470 indstmt
= castNode(IndexStmt
, cmd
->def
);
13471 indoid
= get_constraint_index(oldId
);
13474 TryReuseIndex(indoid
, indstmt
);
13475 /* keep any comment on the index */
13476 indstmt
->idxcomment
= GetComment(indoid
,
13477 RelationRelationId
, 0);
13478 indstmt
->reset_default_tblspc
= true;
13480 cmd
->subtype
= AT_ReAddIndex
;
13481 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
13482 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
13484 /* recreate any comment on the constraint */
13485 RebuildConstraintComment(tab
,
13492 else if (cmd
->subtype
== AT_AddConstraint
)
13494 Constraint
*con
= castNode(Constraint
, cmd
->def
);
13496 con
->old_pktable_oid
= refRelId
;
13497 /* rewriting neither side of a FK */
13498 if (con
->contype
== CONSTR_FOREIGN
&&
13499 !rewrite
&& tab
->rewrite
== 0)
13500 TryReuseForeignKey(oldId
, con
);
13501 con
->reset_default_tblspc
= true;
13502 cmd
->subtype
= AT_ReAddConstraint
;
13503 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13504 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13506 /* recreate any comment on the constraint */
13507 RebuildConstraintComment(tab
,
13508 AT_PASS_OLD_CONSTR
,
13514 else if (cmd
->subtype
== AT_SetNotNull
)
13517 * The parser will create AT_SetNotNull subcommands for
13518 * columns of PRIMARY KEY indexes/constraints, but we need
13519 * not do anything with them here, because the columns'
13520 * NOT NULL marks will already have been propagated into
13521 * the new table definition.
13525 elog(ERROR
, "unexpected statement subtype: %d",
13526 (int) cmd
->subtype
);
13529 else if (IsA(stm
, AlterDomainStmt
))
13531 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
13533 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
13535 Constraint
*con
= castNode(Constraint
, stmt
->def
);
13536 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13538 cmd
->subtype
= AT_ReAddDomainConstraint
;
13539 cmd
->def
= (Node
*) stmt
;
13540 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13541 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13543 /* recreate any comment on the constraint */
13544 RebuildConstraintComment(tab
,
13545 AT_PASS_OLD_CONSTR
,
13552 elog(ERROR
, "unexpected statement subtype: %d",
13553 (int) stmt
->subtype
);
13555 else if (IsA(stm
, CreateStatsStmt
))
13557 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
13558 AlterTableCmd
*newcmd
;
13560 /* keep the statistics object's comment */
13561 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
13563 newcmd
= makeNode(AlterTableCmd
);
13564 newcmd
->subtype
= AT_ReAddStatistics
;
13565 newcmd
->def
= (Node
*) stmt
;
13566 tab
->subcmds
[AT_PASS_MISC
] =
13567 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
13570 elog(ERROR
, "unexpected statement type: %d",
13571 (int) nodeTag(stm
));
13574 relation_close(rel
, NoLock
);
13578 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
13579 * for a table or domain constraint that is being rebuilt.
13581 * objid is the OID of the constraint.
13582 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
13583 * as a string list) for a domain constraint.
13584 * (We could dig that info, as well as the conname, out of the pg_constraint
13585 * entry; but callers already have them so might as well pass them.)
13588 RebuildConstraintComment(AlteredTableInfo
*tab
, int pass
, Oid objid
,
13589 Relation rel
, List
*domname
,
13590 const char *conname
)
13594 AlterTableCmd
*newcmd
;
13596 /* Look for comment for object wanted, and leave if none */
13597 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
13598 if (comment_str
== NULL
)
13601 /* Build CommentStmt node, copying all input data for safety */
13602 cmd
= makeNode(CommentStmt
);
13605 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
13606 cmd
->object
= (Node
*)
13607 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
13608 makeString(pstrdup(RelationGetRelationName(rel
))),
13609 makeString(pstrdup(conname
)));
13613 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
13614 cmd
->object
= (Node
*)
13615 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
13616 makeString(pstrdup(conname
)));
13618 cmd
->comment
= comment_str
;
13620 /* Append it to list of commands */
13621 newcmd
= makeNode(AlterTableCmd
);
13622 newcmd
->subtype
= AT_ReAddComment
;
13623 newcmd
->def
= (Node
*) cmd
;
13624 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
13628 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
13629 * for the real analysis, then mutates the IndexStmt based on that verdict.
13632 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
13634 if (CheckIndexCompatible(oldId
,
13635 stmt
->accessMethod
,
13637 stmt
->excludeOpNames
))
13639 Relation irel
= index_open(oldId
, NoLock
);
13641 /* If it's a partitioned index, there is no storage to share. */
13642 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
13644 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
13645 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
13646 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
13648 index_close(irel
, NoLock
);
13653 * Subroutine for ATPostAlterTypeParse().
13655 * Stash the old P-F equality operator into the Constraint node, for possible
13656 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
13657 * this constraint can be skipped.
13660 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
13669 Assert(con
->contype
== CONSTR_FOREIGN
);
13670 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
13672 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
13673 if (!HeapTupleIsValid(tup
)) /* should not happen */
13674 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
13676 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
13677 Anum_pg_constraint_conpfeqop
);
13678 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
13679 numkeys
= ARR_DIMS(arr
)[0];
13680 /* test follows the one in ri_FetchConstraintInfo() */
13681 if (ARR_NDIM(arr
) != 1 ||
13682 ARR_HASNULL(arr
) ||
13683 ARR_ELEMTYPE(arr
) != OIDOID
)
13684 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
13685 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
13687 /* stash a List of the operator Oids in our Constraint node */
13688 for (i
= 0; i
< numkeys
; i
++)
13689 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
13691 ReleaseSysCache(tup
);
13695 * ALTER COLUMN .. OPTIONS ( ... )
13697 * Returns the address of the modified column
13699 static ObjectAddress
13700 ATExecAlterColumnGenericOptions(Relation rel
,
13701 const char *colName
,
13707 ForeignServer
*server
;
13708 ForeignDataWrapper
*fdw
;
13710 HeapTuple newtuple
;
13712 Datum repl_val
[Natts_pg_attribute
];
13713 bool repl_null
[Natts_pg_attribute
];
13714 bool repl_repl
[Natts_pg_attribute
];
13716 Form_pg_foreign_table fttableform
;
13717 Form_pg_attribute atttableform
;
13719 ObjectAddress address
;
13721 if (options
== NIL
)
13722 return InvalidObjectAddress
;
13724 /* First, determine FDW validator associated to the foreign table. */
13725 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
13726 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
13727 if (!HeapTupleIsValid(tuple
))
13729 (errcode(ERRCODE_UNDEFINED_OBJECT
),
13730 errmsg("foreign table \"%s\" does not exist",
13731 RelationGetRelationName(rel
))));
13732 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
13733 server
= GetForeignServer(fttableform
->ftserver
);
13734 fdw
= GetForeignDataWrapper(server
->fdwid
);
13736 table_close(ftrel
, AccessShareLock
);
13737 ReleaseSysCache(tuple
);
13739 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
13740 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
13741 if (!HeapTupleIsValid(tuple
))
13743 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13744 errmsg("column \"%s\" of relation \"%s\" does not exist",
13745 colName
, RelationGetRelationName(rel
))));
13747 /* Prevent them from altering a system attribute */
13748 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
13749 attnum
= atttableform
->attnum
;
13752 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13753 errmsg("cannot alter system column \"%s\"", colName
)));
13756 /* Initialize buffers for new tuple values */
13757 memset(repl_val
, 0, sizeof(repl_val
));
13758 memset(repl_null
, false, sizeof(repl_null
));
13759 memset(repl_repl
, false, sizeof(repl_repl
));
13761 /* Extract the current options */
13762 datum
= SysCacheGetAttr(ATTNAME
,
13764 Anum_pg_attribute_attfdwoptions
,
13767 datum
= PointerGetDatum(NULL
);
13769 /* Transform the options */
13770 datum
= transformGenericOptions(AttributeRelationId
,
13773 fdw
->fdwvalidator
);
13775 if (PointerIsValid(DatumGetPointer(datum
)))
13776 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
13778 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
13780 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
13782 /* Everything looks good - update the tuple */
13784 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
13785 repl_val
, repl_null
, repl_repl
);
13787 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
13789 InvokeObjectPostAlterHook(RelationRelationId
,
13790 RelationGetRelid(rel
),
13791 atttableform
->attnum
);
13792 ObjectAddressSubSet(address
, RelationRelationId
,
13793 RelationGetRelid(rel
), attnum
);
13795 ReleaseSysCache(tuple
);
13797 table_close(attrel
, RowExclusiveLock
);
13799 heap_freetuple(newtuple
);
13805 * ALTER TABLE OWNER
13807 * recursing is true if we are recursing from a table to its indexes,
13808 * sequences, or toast table. We don't allow the ownership of those things to
13809 * be changed separately from the parent table. Also, we can skip permission
13810 * checks (this is necessary not just an optimization, else we'd fail to
13811 * handle toast tables properly).
13813 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
13814 * free-standing composite type.
13817 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
13819 Relation target_rel
;
13820 Relation class_rel
;
13822 Form_pg_class tuple_class
;
13825 * Get exclusive lock till end of transaction on the target table. Use
13826 * relation_open so that we can work on indexes and sequences.
13828 target_rel
= relation_open(relationOid
, lockmode
);
13830 /* Get its pg_class tuple, too */
13831 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
13833 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
13834 if (!HeapTupleIsValid(tuple
))
13835 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
13836 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
13838 /* Can we change the ownership of this tuple? */
13839 switch (tuple_class
->relkind
)
13841 case RELKIND_RELATION
:
13843 case RELKIND_MATVIEW
:
13844 case RELKIND_FOREIGN_TABLE
:
13845 case RELKIND_PARTITIONED_TABLE
:
13846 /* ok to change owner */
13848 case RELKIND_INDEX
:
13852 * Because ALTER INDEX OWNER used to be allowed, and in fact
13853 * is generated by old versions of pg_dump, we give a warning
13854 * and do nothing rather than erroring out. Also, to avoid
13855 * unnecessary chatter while restoring those old dumps, say
13856 * nothing at all if the command would be a no-op anyway.
13858 if (tuple_class
->relowner
!= newOwnerId
)
13860 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13861 errmsg("cannot change owner of index \"%s\"",
13862 NameStr(tuple_class
->relname
)),
13863 errhint("Change the ownership of the index's table instead.")));
13864 /* quick hack to exit via the no-op path */
13865 newOwnerId
= tuple_class
->relowner
;
13868 case RELKIND_PARTITIONED_INDEX
:
13872 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13873 errmsg("cannot change owner of index \"%s\"",
13874 NameStr(tuple_class
->relname
)),
13875 errhint("Change the ownership of the index's table instead.")));
13877 case RELKIND_SEQUENCE
:
13879 tuple_class
->relowner
!= newOwnerId
)
13881 /* if it's an owned sequence, disallow changing it by itself */
13885 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
13886 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
13888 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13889 errmsg("cannot change owner of sequence \"%s\"",
13890 NameStr(tuple_class
->relname
)),
13891 errdetail("Sequence \"%s\" is linked to table \"%s\".",
13892 NameStr(tuple_class
->relname
),
13893 get_rel_name(tableId
))));
13896 case RELKIND_COMPOSITE_TYPE
:
13900 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13901 errmsg("\"%s\" is a composite type",
13902 NameStr(tuple_class
->relname
)),
13903 errhint("Use ALTER TYPE instead.")));
13905 case RELKIND_TOASTVALUE
:
13911 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13912 errmsg("cannot change owner of relation \"%s\"",
13913 NameStr(tuple_class
->relname
)),
13914 errdetail_relkind_not_supported(tuple_class
->relkind
)));
13918 * If the new owner is the same as the existing owner, consider the
13919 * command to have succeeded. This is for dump restoration purposes.
13921 if (tuple_class
->relowner
!= newOwnerId
)
13923 Datum repl_val
[Natts_pg_class
];
13924 bool repl_null
[Natts_pg_class
];
13925 bool repl_repl
[Natts_pg_class
];
13929 HeapTuple newtuple
;
13931 /* skip permission checks when recursing to index or toast table */
13934 /* Superusers can always do it */
13937 Oid namespaceOid
= tuple_class
->relnamespace
;
13938 AclResult aclresult
;
13940 /* Otherwise, must be owner of the existing object */
13941 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
13942 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
13943 RelationGetRelationName(target_rel
));
13945 /* Must be able to become new owner */
13946 check_can_set_role(GetUserId(), newOwnerId
);
13948 /* New owner must have CREATE privilege on namespace */
13949 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
13951 if (aclresult
!= ACLCHECK_OK
)
13952 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
13953 get_namespace_name(namespaceOid
));
13957 memset(repl_null
, false, sizeof(repl_null
));
13958 memset(repl_repl
, false, sizeof(repl_repl
));
13960 repl_repl
[Anum_pg_class_relowner
- 1] = true;
13961 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
13964 * Determine the modified ACL for the new owner. This is only
13965 * necessary when the ACL is non-null.
13967 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
13968 Anum_pg_class_relacl
,
13972 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
13973 tuple_class
->relowner
, newOwnerId
);
13974 repl_repl
[Anum_pg_class_relacl
- 1] = true;
13975 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
13978 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
13980 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
13982 heap_freetuple(newtuple
);
13985 * We must similarly update any per-column ACLs to reflect the new
13986 * owner; for neatness reasons that's split out as a subroutine.
13988 change_owner_fix_column_acls(relationOid
,
13989 tuple_class
->relowner
,
13993 * Update owner dependency reference, if any. A composite type has
13994 * none, because it's tracked for the pg_type entry instead of here;
13995 * indexes and TOAST tables don't have their own entries either.
13997 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
13998 tuple_class
->relkind
!= RELKIND_INDEX
&&
13999 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
14000 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
14001 changeDependencyOnOwner(RelationRelationId
, relationOid
,
14005 * Also change the ownership of the table's row type, if it has one
14007 if (OidIsValid(tuple_class
->reltype
))
14008 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
14011 * If we are operating on a table or materialized view, also change
14012 * the ownership of any indexes and sequences that belong to the
14013 * relation, as well as its toast table (if it has one).
14015 if (tuple_class
->relkind
== RELKIND_RELATION
||
14016 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
14017 tuple_class
->relkind
== RELKIND_MATVIEW
||
14018 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
14020 List
*index_oid_list
;
14023 /* Find all the indexes belonging to this relation */
14024 index_oid_list
= RelationGetIndexList(target_rel
);
14026 /* For each index, recursively change its ownership */
14027 foreach(i
, index_oid_list
)
14028 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
14030 list_free(index_oid_list
);
14033 /* If it has a toast table, recurse to change its ownership */
14034 if (tuple_class
->reltoastrelid
!= InvalidOid
)
14035 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
14038 /* If it has dependent sequences, recurse to change them too */
14039 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
14042 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
14044 ReleaseSysCache(tuple
);
14045 table_close(class_rel
, RowExclusiveLock
);
14046 relation_close(target_rel
, NoLock
);
14050 * change_owner_fix_column_acls
14052 * Helper function for ATExecChangeOwner. Scan the columns of the table
14053 * and fix any non-null column ACLs to reflect the new owner.
14056 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
14058 Relation attRelation
;
14060 ScanKeyData key
[1];
14061 HeapTuple attributeTuple
;
14063 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
14064 ScanKeyInit(&key
[0],
14065 Anum_pg_attribute_attrelid
,
14066 BTEqualStrategyNumber
, F_OIDEQ
,
14067 ObjectIdGetDatum(relationOid
));
14068 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
14069 true, NULL
, 1, key
);
14070 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
14072 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
14073 Datum repl_val
[Natts_pg_attribute
];
14074 bool repl_null
[Natts_pg_attribute
];
14075 bool repl_repl
[Natts_pg_attribute
];
14079 HeapTuple newtuple
;
14081 /* Ignore dropped columns */
14082 if (att
->attisdropped
)
14085 aclDatum
= heap_getattr(attributeTuple
,
14086 Anum_pg_attribute_attacl
,
14087 RelationGetDescr(attRelation
),
14089 /* Null ACLs do not require changes */
14093 memset(repl_null
, false, sizeof(repl_null
));
14094 memset(repl_repl
, false, sizeof(repl_repl
));
14096 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
14097 oldOwnerId
, newOwnerId
);
14098 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
14099 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
14101 newtuple
= heap_modify_tuple(attributeTuple
,
14102 RelationGetDescr(attRelation
),
14103 repl_val
, repl_null
, repl_repl
);
14105 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
14107 heap_freetuple(newtuple
);
14109 systable_endscan(scan
);
14110 table_close(attRelation
, RowExclusiveLock
);
14114 * change_owner_recurse_to_sequences
14116 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14117 * for sequences that are dependent on serial columns, and changes their
14121 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
14125 ScanKeyData key
[2];
14129 * SERIAL sequences are those having an auto dependency on one of the
14130 * table's columns (we don't care *which* column, exactly).
14132 depRel
= table_open(DependRelationId
, AccessShareLock
);
14134 ScanKeyInit(&key
[0],
14135 Anum_pg_depend_refclassid
,
14136 BTEqualStrategyNumber
, F_OIDEQ
,
14137 ObjectIdGetDatum(RelationRelationId
));
14138 ScanKeyInit(&key
[1],
14139 Anum_pg_depend_refobjid
,
14140 BTEqualStrategyNumber
, F_OIDEQ
,
14141 ObjectIdGetDatum(relationOid
));
14142 /* we leave refobjsubid unspecified */
14144 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
14147 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
14149 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
14152 /* skip dependencies other than auto dependencies on columns */
14153 if (depForm
->refobjsubid
== 0 ||
14154 depForm
->classid
!= RelationRelationId
||
14155 depForm
->objsubid
!= 0 ||
14156 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
14159 /* Use relation_open just in case it's an index */
14160 seqRel
= relation_open(depForm
->objid
, lockmode
);
14162 /* skip non-sequence relations */
14163 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
14165 /* No need to keep the lock */
14166 relation_close(seqRel
, lockmode
);
14170 /* We don't need to close the sequence while we alter it. */
14171 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
14173 /* Now we can close it. Keep the lock till end of transaction. */
14174 relation_close(seqRel
, NoLock
);
14177 systable_endscan(scan
);
14179 relation_close(depRel
, AccessShareLock
);
14183 * ALTER TABLE CLUSTER ON
14185 * The only thing we have to do is to change the indisclustered bits.
14187 * Return the address of the new clustering index.
14189 static ObjectAddress
14190 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
14193 ObjectAddress address
;
14195 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
14197 if (!OidIsValid(indexOid
))
14199 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14200 errmsg("index \"%s\" for table \"%s\" does not exist",
14201 indexName
, RelationGetRelationName(rel
))));
14203 /* Check index is valid to cluster on */
14204 check_index_is_clusterable(rel
, indexOid
, lockmode
);
14206 /* And do the work */
14207 mark_index_clustered(rel
, indexOid
, false);
14209 ObjectAddressSet(address
,
14210 RelationRelationId
, indexOid
);
14216 * ALTER TABLE SET WITHOUT CLUSTER
14218 * We have to find any indexes on the table that have indisclustered bit
14219 * set and turn it off.
14222 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
14224 mark_index_clustered(rel
, InvalidOid
, false);
14228 * Preparation phase for SET ACCESS METHOD
14230 * Check that access method exists. If it is the same as the table's current
14231 * access method, it is a no-op. Otherwise, a table rewrite is necessary.
14234 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
14238 /* Check that the table access method exists */
14239 amoid
= get_table_am_oid(amname
, false);
14241 if (rel
->rd_rel
->relam
== amoid
)
14244 /* Save info for Phase 3 to do the real work */
14245 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
14246 tab
->newAccessMethod
= amoid
;
14250 * ALTER TABLE SET TABLESPACE
14253 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
14257 /* Check that the tablespace exists */
14258 tablespaceId
= get_tablespace_oid(tablespacename
, false);
14260 /* Check permissions except when moving to database's default */
14261 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
14263 AclResult aclresult
;
14265 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
14266 if (aclresult
!= ACLCHECK_OK
)
14267 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
14270 /* Save info for Phase 3 to do the real work */
14271 if (OidIsValid(tab
->newTableSpace
))
14273 (errcode(ERRCODE_SYNTAX_ERROR
),
14274 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14276 tab
->newTableSpace
= tablespaceId
;
14280 * Set, reset, or replace reloptions.
14283 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
14289 HeapTuple newtuple
;
14293 Datum repl_val
[Natts_pg_class
];
14294 bool repl_null
[Natts_pg_class
];
14295 bool repl_repl
[Natts_pg_class
];
14296 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
14298 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
14299 return; /* nothing to do */
14301 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
14303 /* Fetch heap tuple */
14304 relid
= RelationGetRelid(rel
);
14305 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
14306 if (!HeapTupleIsValid(tuple
))
14307 elog(ERROR
, "cache lookup failed for relation %u", relid
);
14309 if (operation
== AT_ReplaceRelOptions
)
14312 * If we're supposed to replace the reloptions list, we just pretend
14313 * there were none before.
14320 /* Get the old reloptions */
14321 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
14325 /* Generate new proposed reloptions (text array) */
14326 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
14327 defList
, NULL
, validnsps
, false,
14328 operation
== AT_ResetRelOptions
);
14331 switch (rel
->rd_rel
->relkind
)
14333 case RELKIND_RELATION
:
14334 case RELKIND_TOASTVALUE
:
14335 case RELKIND_MATVIEW
:
14336 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
14338 case RELKIND_PARTITIONED_TABLE
:
14339 (void) partitioned_table_reloptions(newOptions
, true);
14342 (void) view_reloptions(newOptions
, true);
14344 case RELKIND_INDEX
:
14345 case RELKIND_PARTITIONED_INDEX
:
14346 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
14350 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14351 errmsg("cannot set options for relation \"%s\"",
14352 RelationGetRelationName(rel
)),
14353 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
14357 /* Special-case validation of view options */
14358 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
14360 Query
*view_query
= get_view_query(rel
);
14361 List
*view_options
= untransformRelOptions(newOptions
);
14363 bool check_option
= false;
14365 foreach(cell
, view_options
)
14367 DefElem
*defel
= (DefElem
*) lfirst(cell
);
14369 if (strcmp(defel
->defname
, "check_option") == 0)
14370 check_option
= true;
14374 * If the check option is specified, look to see if the view is
14375 * actually auto-updatable or not.
14379 const char *view_updatable_error
=
14380 view_query_is_auto_updatable(view_query
, true);
14382 if (view_updatable_error
)
14384 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14385 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14386 errhint("%s", _(view_updatable_error
))));
14391 * All we need do here is update the pg_class row; the new options will be
14392 * propagated into relcaches during post-commit cache inval.
14394 memset(repl_val
, 0, sizeof(repl_val
));
14395 memset(repl_null
, false, sizeof(repl_null
));
14396 memset(repl_repl
, false, sizeof(repl_repl
));
14398 if (newOptions
!= (Datum
) 0)
14399 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
14401 repl_null
[Anum_pg_class_reloptions
- 1] = true;
14403 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
14405 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
14406 repl_val
, repl_null
, repl_repl
);
14408 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
14410 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
14412 heap_freetuple(newtuple
);
14414 ReleaseSysCache(tuple
);
14416 /* repeat the whole exercise for the toast table, if there's one */
14417 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
14420 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
14422 toastrel
= table_open(toastid
, lockmode
);
14424 /* Fetch heap tuple */
14425 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
14426 if (!HeapTupleIsValid(tuple
))
14427 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
14429 if (operation
== AT_ReplaceRelOptions
)
14432 * If we're supposed to replace the reloptions list, we just
14433 * pretend there were none before.
14440 /* Get the old reloptions */
14441 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
14445 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
14446 defList
, "toast", validnsps
, false,
14447 operation
== AT_ResetRelOptions
);
14449 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
14451 memset(repl_val
, 0, sizeof(repl_val
));
14452 memset(repl_null
, false, sizeof(repl_null
));
14453 memset(repl_repl
, false, sizeof(repl_repl
));
14455 if (newOptions
!= (Datum
) 0)
14456 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
14458 repl_null
[Anum_pg_class_reloptions
- 1] = true;
14460 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
14462 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
14463 repl_val
, repl_null
, repl_repl
);
14465 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
14467 InvokeObjectPostAlterHookArg(RelationRelationId
,
14468 RelationGetRelid(toastrel
), 0,
14471 heap_freetuple(newtuple
);
14473 ReleaseSysCache(tuple
);
14475 table_close(toastrel
, NoLock
);
14478 table_close(pgclass
, RowExclusiveLock
);
14482 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
14483 * rewriting to be done, so we just want to copy the data as fast as possible.
14486 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
14490 RelFileNumber newrelfilenumber
;
14491 RelFileLocator newrlocator
;
14492 List
*reltoastidxids
= NIL
;
14496 * Need lock here in case we are recursing to toast table or index
14498 rel
= relation_open(tableOid
, lockmode
);
14500 /* Check first if relation can be moved to new tablespace */
14501 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
14503 InvokeObjectPostAlterHook(RelationRelationId
,
14504 RelationGetRelid(rel
), 0);
14505 relation_close(rel
, NoLock
);
14509 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
14510 /* Fetch the list of indexes on toast relation if necessary */
14511 if (OidIsValid(reltoastrelid
))
14513 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
14515 reltoastidxids
= RelationGetIndexList(toastRel
);
14516 relation_close(toastRel
, lockmode
);
14520 * Relfilenumbers are not unique in databases across tablespaces, so we
14521 * need to allocate a new one in the new tablespace.
14523 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
14524 rel
->rd_rel
->relpersistence
);
14526 /* Open old and new relation */
14527 newrlocator
= rel
->rd_locator
;
14528 newrlocator
.relNumber
= newrelfilenumber
;
14529 newrlocator
.spcOid
= newTableSpace
;
14531 /* hand off to AM to actually create new rel storage and copy the data */
14532 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
14534 index_copy_data(rel
, newrlocator
);
14538 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
14539 table_relation_copy_data(rel
, &newrlocator
);
14543 * Update the pg_class row.
14545 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
14546 * executed on pg_class or its indexes (the above copy wouldn't contain
14547 * the updated pg_class entry), but that's forbidden with
14548 * CheckRelationTableSpaceMove().
14550 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
14552 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
14554 RelationAssumeNewRelfilelocator(rel
);
14556 relation_close(rel
, NoLock
);
14558 /* Make sure the reltablespace change is visible */
14559 CommandCounterIncrement();
14561 /* Move associated toast relation and/or indexes, too */
14562 if (OidIsValid(reltoastrelid
))
14563 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
14564 foreach(lc
, reltoastidxids
)
14565 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
14568 list_free(reltoastidxids
);
14572 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
14573 * storage that have an interest in preserving tablespace.
14575 * Since these have no storage the tablespace can be updated with a simple
14576 * metadata only operation to update the tablespace.
14579 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
14582 * Shouldn't be called on relations having storage; these are processed in
14585 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
14587 /* check if relation can be moved to its new tablespace */
14588 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
14590 InvokeObjectPostAlterHook(RelationRelationId
,
14591 RelationGetRelid(rel
),
14596 /* Update can be done, so change reltablespace */
14597 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
14599 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
14601 /* Make sure the reltablespace change is visible */
14602 CommandCounterIncrement();
14606 * Alter Table ALL ... SET TABLESPACE
14608 * Allows a user to move all objects of some type in a given tablespace in the
14609 * current database to another tablespace. Objects can be chosen based on the
14610 * owner of the object also, to allow users to move only their objects.
14611 * The user must have CREATE rights on the new tablespace, as usual. The main
14612 * permissions handling is done by the lower-level table move function.
14614 * All to-be-moved objects are locked first. If NOWAIT is specified and the
14615 * lock can't be acquired then we ereport(ERROR).
14618 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
14620 List
*relations
= NIL
;
14622 ScanKeyData key
[1];
14624 TableScanDesc scan
;
14626 Oid orig_tablespaceoid
;
14627 Oid new_tablespaceoid
;
14628 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
14630 /* Ensure we were not asked to move something we can't */
14631 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
14632 stmt
->objtype
!= OBJECT_MATVIEW
)
14634 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
14635 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
14637 /* Get the orig and new tablespace OIDs */
14638 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
14639 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
14641 /* Can't move shared relations in to or out of pg_global */
14642 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
14643 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
14644 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
14646 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
14647 errmsg("cannot move relations in to or out of pg_global tablespace")));
14650 * Must have CREATE rights on the new tablespace, unless it is the
14651 * database default tablespace (which all users implicitly have CREATE
14654 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
14656 AclResult aclresult
;
14658 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
14660 if (aclresult
!= ACLCHECK_OK
)
14661 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
14662 get_tablespace_name(new_tablespaceoid
));
14666 * Now that the checks are done, check if we should set either to
14667 * InvalidOid because it is our database's default tablespace.
14669 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
14670 orig_tablespaceoid
= InvalidOid
;
14672 if (new_tablespaceoid
== MyDatabaseTableSpace
)
14673 new_tablespaceoid
= InvalidOid
;
14676 if (orig_tablespaceoid
== new_tablespaceoid
)
14677 return new_tablespaceoid
;
14680 * Walk the list of objects in the tablespace and move them. This will
14681 * only find objects in our database, of course.
14683 ScanKeyInit(&key
[0],
14684 Anum_pg_class_reltablespace
,
14685 BTEqualStrategyNumber
, F_OIDEQ
,
14686 ObjectIdGetDatum(orig_tablespaceoid
));
14688 rel
= table_open(RelationRelationId
, AccessShareLock
);
14689 scan
= table_beginscan_catalog(rel
, 1, key
);
14690 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
14692 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
14693 Oid relOid
= relForm
->oid
;
14696 * Do not move objects in pg_catalog as part of this, if an admin
14697 * really wishes to do so, they can issue the individual ALTER
14698 * commands directly.
14700 * Also, explicitly avoid any shared tables, temp tables, or TOAST
14701 * (TOAST will be moved with the main table).
14703 if (IsCatalogNamespace(relForm
->relnamespace
) ||
14704 relForm
->relisshared
||
14705 isAnyTempNamespace(relForm
->relnamespace
) ||
14706 IsToastNamespace(relForm
->relnamespace
))
14709 /* Only move the object type requested */
14710 if ((stmt
->objtype
== OBJECT_TABLE
&&
14711 relForm
->relkind
!= RELKIND_RELATION
&&
14712 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
14713 (stmt
->objtype
== OBJECT_INDEX
&&
14714 relForm
->relkind
!= RELKIND_INDEX
&&
14715 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
14716 (stmt
->objtype
== OBJECT_MATVIEW
&&
14717 relForm
->relkind
!= RELKIND_MATVIEW
))
14720 /* Check if we are only moving objects owned by certain roles */
14721 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
14725 * Handle permissions-checking here since we are locking the tables
14726 * and also to avoid doing a bunch of work only to fail part-way. Note
14727 * that permissions will also be checked by AlterTableInternal().
14729 * Caller must be considered an owner on the table to move it.
14731 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
14732 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
14733 NameStr(relForm
->relname
));
14735 if (stmt
->nowait
&&
14736 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
14738 (errcode(ERRCODE_OBJECT_IN_USE
),
14739 errmsg("aborting because lock on relation \"%s.%s\" is not available",
14740 get_namespace_name(relForm
->relnamespace
),
14741 NameStr(relForm
->relname
))));
14743 LockRelationOid(relOid
, AccessExclusiveLock
);
14745 /* Add to our list of objects to move */
14746 relations
= lappend_oid(relations
, relOid
);
14749 table_endscan(scan
);
14750 table_close(rel
, AccessShareLock
);
14752 if (relations
== NIL
)
14754 (errcode(ERRCODE_NO_DATA_FOUND
),
14755 errmsg("no matching relations in tablespace \"%s\" found",
14756 orig_tablespaceoid
== InvalidOid
? "(database default)" :
14757 get_tablespace_name(orig_tablespaceoid
))));
14759 /* Everything is locked, loop through and move all of the relations. */
14760 foreach(l
, relations
)
14763 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14765 cmd
->subtype
= AT_SetTableSpace
;
14766 cmd
->name
= stmt
->new_tablespacename
;
14768 cmds
= lappend(cmds
, cmd
);
14770 EventTriggerAlterTableStart((Node
*) stmt
);
14771 /* OID is set by AlterTableInternal */
14772 AlterTableInternal(lfirst_oid(l
), cmds
, false);
14773 EventTriggerAlterTableEnd();
14776 return new_tablespaceoid
;
14780 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
14782 SMgrRelation dstrel
;
14784 dstrel
= smgropen(newrlocator
, rel
->rd_backend
);
14787 * Since we copy the file directly without looking at the shared buffers,
14788 * we'd better first flush out any pages of the source relation that are
14789 * in shared buffers. We assume no new changes will be made while we are
14790 * holding exclusive lock on the rel.
14792 FlushRelationBuffers(rel
);
14795 * Create and copy all forks of the relation, and schedule unlinking of
14796 * old physical files.
14798 * NOTE: any conflict in relfilenumber value will be caught in
14799 * RelationCreateStorage().
14801 RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
14803 /* copy main fork */
14804 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
14805 rel
->rd_rel
->relpersistence
);
14807 /* copy those extra forks that exist */
14808 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
14809 forkNum
<= MAX_FORKNUM
; forkNum
++)
14811 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
14813 smgrcreate(dstrel
, forkNum
, false);
14816 * WAL log creation if the relation is persistent, or this is the
14817 * init fork of an unlogged relation.
14819 if (RelationIsPermanent(rel
) ||
14820 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
14821 forkNum
== INIT_FORKNUM
))
14822 log_smgrcreate(&newrlocator
, forkNum
);
14823 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
14824 rel
->rd_rel
->relpersistence
);
14828 /* drop old relation, and close new one */
14829 RelationDropStorage(rel
);
14834 * ALTER TABLE ENABLE/DISABLE TRIGGER
14836 * We just pass this off to trigger.c.
14839 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
14840 char fires_when
, bool skip_system
, bool recurse
,
14843 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
14844 fires_when
, skip_system
, recurse
,
14847 InvokeObjectPostAlterHook(RelationRelationId
,
14848 RelationGetRelid(rel
), 0);
14852 * ALTER TABLE ENABLE/DISABLE RULE
14854 * We just pass this off to rewriteDefine.c.
14857 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
14858 char fires_when
, LOCKMODE lockmode
)
14860 EnableDisableRule(rel
, rulename
, fires_when
);
14862 InvokeObjectPostAlterHook(RelationRelationId
,
14863 RelationGetRelid(rel
), 0);
14867 * ALTER TABLE INHERIT
14869 * Add a parent to the child's parents. This verifies that all the columns and
14870 * check constraints of the parent appear in the child and that they have the
14871 * same data types and expressions.
14874 ATPrepAddInherit(Relation child_rel
)
14876 if (child_rel
->rd_rel
->reloftype
)
14878 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14879 errmsg("cannot change inheritance of typed table")));
14881 if (child_rel
->rd_rel
->relispartition
)
14883 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14884 errmsg("cannot change inheritance of a partition")));
14886 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
14888 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14889 errmsg("cannot change inheritance of partitioned table")));
14893 * Return the address of the new parent relation.
14895 static ObjectAddress
14896 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
14898 Relation parent_rel
;
14900 ObjectAddress address
;
14901 const char *trigger_name
;
14904 * A self-exclusive lock is needed here. See the similar case in
14905 * MergeAttributes() for a full explanation.
14907 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
14910 * Must be owner of both parent and child -- child was checked by
14911 * ATSimplePermissions call in ATPrepCmd
14913 ATSimplePermissions(AT_AddInherit
, parent_rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
14915 /* Permanent rels cannot inherit from temporary ones */
14916 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
14917 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
14919 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14920 errmsg("cannot inherit from temporary relation \"%s\"",
14921 RelationGetRelationName(parent_rel
))));
14923 /* If parent rel is temp, it must belong to this session */
14924 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
14925 !parent_rel
->rd_islocaltemp
)
14927 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14928 errmsg("cannot inherit from temporary relation of another session")));
14930 /* Ditto for the child */
14931 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
14932 !child_rel
->rd_islocaltemp
)
14934 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14935 errmsg("cannot inherit to temporary relation of another session")));
14937 /* Prevent partitioned tables from becoming inheritance parents */
14938 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
14940 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14941 errmsg("cannot inherit from partitioned table \"%s\"",
14942 parent
->relname
)));
14944 /* Likewise for partitions */
14945 if (parent_rel
->rd_rel
->relispartition
)
14947 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14948 errmsg("cannot inherit from a partition")));
14951 * Prevent circularity by seeing if proposed parent inherits from child.
14952 * (In particular, this disallows making a rel inherit from itself.)
14954 * This is not completely bulletproof because of race conditions: in
14955 * multi-level inheritance trees, someone else could concurrently be
14956 * making another inheritance link that closes the loop but does not join
14957 * either of the rels we have locked. Preventing that seems to require
14958 * exclusive locks on the entire inheritance tree, which is a cure worse
14959 * than the disease. find_all_inheritors() will cope with circularity
14960 * anyway, so don't sweat it too much.
14962 * We use weakest lock we can on child's children, namely AccessShareLock.
14964 children
= find_all_inheritors(RelationGetRelid(child_rel
),
14965 AccessShareLock
, NULL
);
14967 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
14969 (errcode(ERRCODE_DUPLICATE_TABLE
),
14970 errmsg("circular inheritance not allowed"),
14971 errdetail("\"%s\" is already a child of \"%s\".",
14973 RelationGetRelationName(child_rel
))));
14976 * If child_rel has row-level triggers with transition tables, we
14977 * currently don't allow it to become an inheritance child. See also
14978 * prohibitions in ATExecAttachPartition() and CreateTrigger().
14980 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
14981 if (trigger_name
!= NULL
)
14983 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14984 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
14985 trigger_name
, RelationGetRelationName(child_rel
)),
14986 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
14988 /* OK to create inheritance */
14989 CreateInheritance(child_rel
, parent_rel
);
14991 ObjectAddressSet(address
, RelationRelationId
,
14992 RelationGetRelid(parent_rel
));
14994 /* keep our lock on the parent relation until commit */
14995 table_close(parent_rel
, NoLock
);
15001 * CreateInheritance
15002 * Catalog manipulation portion of creating inheritance between a child
15003 * table and a parent table.
15005 * Common to ATExecAddInherit() and ATExecAttachPartition().
15008 CreateInheritance(Relation child_rel
, Relation parent_rel
)
15010 Relation catalogRelation
;
15013 HeapTuple inheritsTuple
;
15016 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15017 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
15020 * Check for duplicates in the list of parents, and determine the highest
15021 * inhseqno already present; we'll use the next one for the new parent.
15022 * Also, if proposed child is a partition, it cannot already be
15025 * Note: we do not reject the case where the child already inherits from
15026 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15029 Anum_pg_inherits_inhrelid
,
15030 BTEqualStrategyNumber
, F_OIDEQ
,
15031 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15032 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
15033 true, NULL
, 1, &key
);
15035 /* inhseqno sequences start at 1 */
15037 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
15039 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
15041 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
15043 (errcode(ERRCODE_DUPLICATE_TABLE
),
15044 errmsg("relation \"%s\" would be inherited from more than once",
15045 RelationGetRelationName(parent_rel
))));
15047 if (inh
->inhseqno
> inhseqno
)
15048 inhseqno
= inh
->inhseqno
;
15050 systable_endscan(scan
);
15052 /* Match up the columns and bump attinhcount as needed */
15053 MergeAttributesIntoExisting(child_rel
, parent_rel
);
15055 /* Match up the constraints and bump coninhcount as needed */
15056 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
15059 * OK, it looks valid. Make the catalog entries that show inheritance.
15061 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
15062 RelationGetRelid(parent_rel
),
15065 parent_rel
->rd_rel
->relkind
==
15066 RELKIND_PARTITIONED_TABLE
);
15068 /* Now we're done with pg_inherits */
15069 table_close(catalogRelation
, RowExclusiveLock
);
15073 * Obtain the source-text form of the constraint expression for a check
15074 * constraint, given its pg_constraint tuple
15077 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
15079 Form_pg_constraint con
;
15084 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
15085 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
15087 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
15089 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
15090 ObjectIdGetDatum(con
->conrelid
));
15091 return TextDatumGetCString(expr
);
15095 * Determine whether two check constraints are functionally equivalent
15097 * The test we apply is to see whether they reverse-compile to the same
15098 * source string. This insulates us from issues like whether attributes
15099 * have the same physical column numbers in parent and child relations.
15102 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
15104 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
15105 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
15107 if (acon
->condeferrable
!= bcon
->condeferrable
||
15108 acon
->condeferred
!= bcon
->condeferred
||
15109 strcmp(decompile_conbin(a
, tupleDesc
),
15110 decompile_conbin(b
, tupleDesc
)) != 0)
15117 * Check columns in child table match up with columns in parent, and increment
15118 * their attinhcount.
15120 * Called by CreateInheritance
15122 * Currently all parent columns must be found in child. Missing columns are an
15123 * error. One day we might consider creating new columns like CREATE TABLE
15124 * does. However, that is widely unpopular --- in the common use case of
15125 * partitioned tables it's a foot-gun.
15127 * The data type must match exactly. If the parent column is NOT NULL then
15128 * the child must be as well. Defaults are not compared, however.
15131 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
)
15134 AttrNumber parent_attno
;
15136 TupleDesc tupleDesc
;
15138 bool child_is_partition
= false;
15140 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
15142 tupleDesc
= RelationGetDescr(parent_rel
);
15143 parent_natts
= tupleDesc
->natts
;
15145 /* If parent_rel is a partitioned table, child_rel must be a partition */
15146 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15147 child_is_partition
= true;
15149 for (parent_attno
= 1; parent_attno
<= parent_natts
; parent_attno
++)
15151 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
15153 char *attributeName
= NameStr(attribute
->attname
);
15155 /* Ignore dropped columns in the parent. */
15156 if (attribute
->attisdropped
)
15159 /* Find same column in child (matching on column name). */
15160 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
),
15162 if (HeapTupleIsValid(tuple
))
15164 /* Check they are same type, typmod, and collation */
15165 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
15167 if (attribute
->atttypid
!= childatt
->atttypid
||
15168 attribute
->atttypmod
!= childatt
->atttypmod
)
15170 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15171 errmsg("child table \"%s\" has different type for column \"%s\"",
15172 RelationGetRelationName(child_rel
),
15175 if (attribute
->attcollation
!= childatt
->attcollation
)
15177 (errcode(ERRCODE_COLLATION_MISMATCH
),
15178 errmsg("child table \"%s\" has different collation for column \"%s\"",
15179 RelationGetRelationName(child_rel
),
15183 * Check child doesn't discard NOT NULL property. (Other
15184 * constraints are checked elsewhere.)
15186 if (attribute
->attnotnull
&& !childatt
->attnotnull
)
15188 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15189 errmsg("column \"%s\" in child table must be marked NOT NULL",
15193 * Child column must be generated if and only if parent column is.
15195 if (attribute
->attgenerated
&& !childatt
->attgenerated
)
15197 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15198 errmsg("column \"%s\" in child table must be a generated column",
15200 if (childatt
->attgenerated
&& !attribute
->attgenerated
)
15202 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15203 errmsg("column \"%s\" in child table must not be a generated column",
15207 * OK, bump the child column's inheritance count. (If we fail
15208 * later on, this change will just roll back.)
15210 childatt
->attinhcount
++;
15211 if (childatt
->attinhcount
< 0)
15213 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
15214 errmsg("too many inheritance parents"));
15217 * In case of partitions, we must enforce that value of attislocal
15218 * is same in all partitions. (Note: there are only inherited
15219 * attributes in partitions)
15221 if (child_is_partition
)
15223 Assert(childatt
->attinhcount
== 1);
15224 childatt
->attislocal
= false;
15227 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
15228 heap_freetuple(tuple
);
15233 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15234 errmsg("child table is missing column \"%s\"",
15239 table_close(attrrel
, RowExclusiveLock
);
15243 * Check constraints in child table match up with constraints in parent,
15244 * and increment their coninhcount.
15246 * Constraints that are marked ONLY in the parent are ignored.
15248 * Called by CreateInheritance
15250 * Currently all constraints in parent must be present in the child. One day we
15251 * may consider adding new constraints like CREATE TABLE does.
15253 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15254 * constraints. As long as tables have more like 10 constraints it shouldn't be
15255 * a problem though. Even 100 constraints ought not be the end of the world.
15257 * XXX See MergeWithExistingConstraint too if you change this code.
15260 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
15262 Relation catalog_relation
;
15263 TupleDesc tuple_desc
;
15264 SysScanDesc parent_scan
;
15265 ScanKeyData parent_key
;
15266 HeapTuple parent_tuple
;
15267 bool child_is_partition
= false;
15269 catalog_relation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
15270 tuple_desc
= RelationGetDescr(catalog_relation
);
15272 /* If parent_rel is a partitioned table, child_rel must be a partition */
15273 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15274 child_is_partition
= true;
15276 /* Outer loop scans through the parent's constraint definitions */
15277 ScanKeyInit(&parent_key
,
15278 Anum_pg_constraint_conrelid
,
15279 BTEqualStrategyNumber
, F_OIDEQ
,
15280 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
15281 parent_scan
= systable_beginscan(catalog_relation
, ConstraintRelidTypidNameIndexId
,
15282 true, NULL
, 1, &parent_key
);
15284 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
15286 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
15287 SysScanDesc child_scan
;
15288 ScanKeyData child_key
;
15289 HeapTuple child_tuple
;
15290 bool found
= false;
15292 if (parent_con
->contype
!= CONSTRAINT_CHECK
)
15295 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15296 if (parent_con
->connoinherit
)
15299 /* Search for a child constraint matching this one */
15300 ScanKeyInit(&child_key
,
15301 Anum_pg_constraint_conrelid
,
15302 BTEqualStrategyNumber
, F_OIDEQ
,
15303 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15304 child_scan
= systable_beginscan(catalog_relation
, ConstraintRelidTypidNameIndexId
,
15305 true, NULL
, 1, &child_key
);
15307 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
15309 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
15310 HeapTuple child_copy
;
15312 if (child_con
->contype
!= CONSTRAINT_CHECK
)
15315 if (strcmp(NameStr(parent_con
->conname
),
15316 NameStr(child_con
->conname
)) != 0)
15319 if (!constraints_equivalent(parent_tuple
, child_tuple
, tuple_desc
))
15321 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15322 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15323 RelationGetRelationName(child_rel
),
15324 NameStr(parent_con
->conname
))));
15326 /* If the child constraint is "no inherit" then cannot merge */
15327 if (child_con
->connoinherit
)
15329 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
15330 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15331 NameStr(child_con
->conname
),
15332 RelationGetRelationName(child_rel
))));
15335 * If the child constraint is "not valid" then cannot merge with a
15336 * valid parent constraint
15338 if (parent_con
->convalidated
&& !child_con
->convalidated
)
15340 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
15341 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15342 NameStr(child_con
->conname
),
15343 RelationGetRelationName(child_rel
))));
15346 * OK, bump the child constraint's inheritance count. (If we fail
15347 * later on, this change will just roll back.)
15349 child_copy
= heap_copytuple(child_tuple
);
15350 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
15351 child_con
->coninhcount
++;
15352 if (child_con
->coninhcount
< 0)
15354 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
15355 errmsg("too many inheritance parents"));
15358 * In case of partitions, an inherited constraint must be
15359 * inherited only once since it cannot have multiple parents and
15360 * it is never considered local.
15362 if (child_is_partition
)
15364 Assert(child_con
->coninhcount
== 1);
15365 child_con
->conislocal
= false;
15368 CatalogTupleUpdate(catalog_relation
, &child_copy
->t_self
, child_copy
);
15369 heap_freetuple(child_copy
);
15375 systable_endscan(child_scan
);
15379 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15380 errmsg("child table is missing constraint \"%s\"",
15381 NameStr(parent_con
->conname
))));
15384 systable_endscan(parent_scan
);
15385 table_close(catalog_relation
, RowExclusiveLock
);
15389 * ALTER TABLE NO INHERIT
15391 * Return value is the address of the relation that is no longer parent.
15393 static ObjectAddress
15394 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
15396 ObjectAddress address
;
15397 Relation parent_rel
;
15399 if (rel
->rd_rel
->relispartition
)
15401 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15402 errmsg("cannot change inheritance of a partition")));
15405 * AccessShareLock on the parent is probably enough, seeing that DROP
15406 * TABLE doesn't lock parent tables at all. We need some lock since we'll
15407 * be inspecting the parent's schema.
15409 parent_rel
= table_openrv(parent
, AccessShareLock
);
15412 * We don't bother to check ownership of the parent table --- ownership of
15413 * the child is presumed enough rights.
15416 /* Off to RemoveInheritance() where most of the work happens */
15417 RemoveInheritance(rel
, parent_rel
, false);
15419 ObjectAddressSet(address
, RelationRelationId
,
15420 RelationGetRelid(parent_rel
));
15422 /* keep our lock on the parent relation until commit */
15423 table_close(parent_rel
, NoLock
);
15429 * MarkInheritDetached
15431 * Set inhdetachpending for a partition, for ATExecDetachPartition
15432 * in concurrent mode. While at it, verify that no other partition is
15433 * already pending detach.
15436 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
15438 Relation catalogRelation
;
15441 HeapTuple inheritsTuple
;
15442 bool found
= false;
15444 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
15447 * Find pg_inherits entries by inhparent. (We need to scan them all in
15448 * order to verify that no other partition is pending detach.)
15450 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
15452 Anum_pg_inherits_inhparent
,
15453 BTEqualStrategyNumber
, F_OIDEQ
,
15454 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
15455 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
15456 true, NULL
, 1, &key
);
15458 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
15460 Form_pg_inherits inhForm
;
15462 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
15463 if (inhForm
->inhdetachpending
)
15465 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
15466 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
15467 get_rel_name(inhForm
->inhrelid
),
15468 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
15469 RelationGetRelationName(parent_rel
)),
15470 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
15472 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
15476 newtup
= heap_copytuple(inheritsTuple
);
15477 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
15479 CatalogTupleUpdate(catalogRelation
,
15480 &inheritsTuple
->t_self
,
15483 heap_freetuple(newtup
);
15484 /* keep looking, to ensure we catch others pending detach */
15489 systable_endscan(scan
);
15490 table_close(catalogRelation
, RowExclusiveLock
);
15494 (errcode(ERRCODE_UNDEFINED_TABLE
),
15495 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
15496 RelationGetRelationName(child_rel
),
15497 RelationGetRelationName(parent_rel
))));
15501 * RemoveInheritance
15503 * Drop a parent from the child's parents. This just adjusts the attinhcount
15504 * and attislocal of the columns and removes the pg_inherit and pg_depend
15505 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
15507 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
15508 * up attislocal stays true, which means if a child is ever removed from a
15509 * parent then its columns will never be automatically dropped which may
15510 * surprise. But at least we'll never surprise by dropping columns someone
15511 * isn't expecting to be dropped which would actually mean data loss.
15513 * coninhcount and conislocal for inherited constraints are adjusted in
15514 * exactly the same way.
15516 * Common to ATExecDropInherit() and ATExecDetachPartition().
15519 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
15521 Relation catalogRelation
;
15523 ScanKeyData key
[3];
15524 HeapTuple attributeTuple
,
15528 bool child_is_partition
= false;
15530 /* If parent_rel is a partitioned table, child_rel must be a partition */
15531 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15532 child_is_partition
= true;
15534 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
15535 RelationGetRelid(parent_rel
),
15537 RelationGetRelationName(child_rel
));
15540 if (child_is_partition
)
15542 (errcode(ERRCODE_UNDEFINED_TABLE
),
15543 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
15544 RelationGetRelationName(child_rel
),
15545 RelationGetRelationName(parent_rel
))));
15548 (errcode(ERRCODE_UNDEFINED_TABLE
),
15549 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
15550 RelationGetRelationName(parent_rel
),
15551 RelationGetRelationName(child_rel
))));
15555 * Search through child columns looking for ones matching parent rel
15557 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
15558 ScanKeyInit(&key
[0],
15559 Anum_pg_attribute_attrelid
,
15560 BTEqualStrategyNumber
, F_OIDEQ
,
15561 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15562 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
15563 true, NULL
, 1, key
);
15564 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
15566 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
15568 /* Ignore if dropped or not inherited */
15569 if (att
->attisdropped
)
15571 if (att
->attinhcount
<= 0)
15574 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
15575 NameStr(att
->attname
)))
15577 /* Decrement inhcount and possibly set islocal to true */
15578 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
15579 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
15581 copy_att
->attinhcount
--;
15582 if (copy_att
->attinhcount
== 0)
15583 copy_att
->attislocal
= true;
15585 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
15586 heap_freetuple(copyTuple
);
15589 systable_endscan(scan
);
15590 table_close(catalogRelation
, RowExclusiveLock
);
15593 * Likewise, find inherited check constraints and disinherit them. To do
15594 * this, we first need a list of the names of the parent's check
15595 * constraints. (We cheat a bit by only checking for name matches,
15596 * assuming that the expressions will match.)
15598 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
15599 ScanKeyInit(&key
[0],
15600 Anum_pg_constraint_conrelid
,
15601 BTEqualStrategyNumber
, F_OIDEQ
,
15602 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
15603 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
15604 true, NULL
, 1, key
);
15608 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
15610 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
15612 if (con
->contype
== CONSTRAINT_CHECK
)
15613 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
15616 systable_endscan(scan
);
15618 /* Now scan the child's constraints */
15619 ScanKeyInit(&key
[0],
15620 Anum_pg_constraint_conrelid
,
15621 BTEqualStrategyNumber
, F_OIDEQ
,
15622 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15623 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
15624 true, NULL
, 1, key
);
15626 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
15628 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
15632 if (con
->contype
!= CONSTRAINT_CHECK
)
15636 foreach(lc
, connames
)
15638 if (strcmp(NameStr(con
->conname
), (char *) lfirst(lc
)) == 0)
15647 /* Decrement inhcount and possibly set islocal to true */
15648 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
15649 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
15651 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
15652 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
15653 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
15655 copy_con
->coninhcount
--;
15656 if (copy_con
->coninhcount
== 0)
15657 copy_con
->conislocal
= true;
15659 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
15660 heap_freetuple(copyTuple
);
15664 systable_endscan(scan
);
15665 table_close(catalogRelation
, RowExclusiveLock
);
15667 drop_parent_dependency(RelationGetRelid(child_rel
),
15668 RelationRelationId
,
15669 RelationGetRelid(parent_rel
),
15670 child_dependency_type(child_is_partition
));
15673 * Post alter hook of this inherits. Since object_access_hook doesn't take
15674 * multiple object identifiers, we relay oid of parent relation using
15675 * auxiliary_id argument.
15677 InvokeObjectPostAlterHookArg(InheritsRelationId
,
15678 RelationGetRelid(child_rel
), 0,
15679 RelationGetRelid(parent_rel
), false);
15683 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
15684 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
15685 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
15686 * be TypeRelationId). There's no convenient way to do this, so go trawling
15687 * through pg_depend.
15690 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
15691 DependencyType deptype
)
15693 Relation catalogRelation
;
15695 ScanKeyData key
[3];
15696 HeapTuple depTuple
;
15698 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
15700 ScanKeyInit(&key
[0],
15701 Anum_pg_depend_classid
,
15702 BTEqualStrategyNumber
, F_OIDEQ
,
15703 ObjectIdGetDatum(RelationRelationId
));
15704 ScanKeyInit(&key
[1],
15705 Anum_pg_depend_objid
,
15706 BTEqualStrategyNumber
, F_OIDEQ
,
15707 ObjectIdGetDatum(relid
));
15708 ScanKeyInit(&key
[2],
15709 Anum_pg_depend_objsubid
,
15710 BTEqualStrategyNumber
, F_INT4EQ
,
15713 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
15716 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
15718 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
15720 if (dep
->refclassid
== refclassid
&&
15721 dep
->refobjid
== refobjid
&&
15722 dep
->refobjsubid
== 0 &&
15723 dep
->deptype
== deptype
)
15724 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
15727 systable_endscan(scan
);
15728 table_close(catalogRelation
, RowExclusiveLock
);
15734 * Attach a table to a composite type, as though it had been created with CREATE
15735 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
15736 * subject table must not have inheritance parents. These restrictions ensure
15737 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
15739 * The address of the type is returned.
15741 static ObjectAddress
15742 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
15744 Oid relid
= RelationGetRelid(rel
);
15746 Form_pg_type typeform
;
15748 Relation inheritsRelation
,
15752 AttrNumber table_attno
,
15754 TupleDesc typeTupleDesc
,
15756 ObjectAddress tableobj
,
15758 HeapTuple classtuple
;
15760 /* Validate the type. */
15761 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
15762 check_of_type(typetuple
);
15763 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
15764 typeid = typeform
->oid
;
15766 /* Fail if the table has any inheritance parents. */
15767 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
15769 Anum_pg_inherits_inhrelid
,
15770 BTEqualStrategyNumber
, F_OIDEQ
,
15771 ObjectIdGetDatum(relid
));
15772 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
15773 true, NULL
, 1, &key
);
15774 if (HeapTupleIsValid(systable_getnext(scan
)))
15776 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15777 errmsg("typed tables cannot inherit")));
15778 systable_endscan(scan
);
15779 table_close(inheritsRelation
, AccessShareLock
);
15782 * Check the tuple descriptors for compatibility. Unlike inheritance, we
15783 * require that the order also match. However, attnotnull need not match.
15785 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
15786 tableTupleDesc
= RelationGetDescr(rel
);
15788 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
15790 Form_pg_attribute type_attr
,
15792 const char *type_attname
,
15795 /* Get the next non-dropped type attribute. */
15796 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
15797 if (type_attr
->attisdropped
)
15799 type_attname
= NameStr(type_attr
->attname
);
15801 /* Get the next non-dropped table attribute. */
15804 if (table_attno
> tableTupleDesc
->natts
)
15806 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15807 errmsg("table is missing column \"%s\"",
15809 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
15811 } while (table_attr
->attisdropped
);
15812 table_attname
= NameStr(table_attr
->attname
);
15814 /* Compare name. */
15815 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
15817 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15818 errmsg("table has column \"%s\" where type requires \"%s\"",
15819 table_attname
, type_attname
)));
15821 /* Compare type. */
15822 if (table_attr
->atttypid
!= type_attr
->atttypid
||
15823 table_attr
->atttypmod
!= type_attr
->atttypmod
||
15824 table_attr
->attcollation
!= type_attr
->attcollation
)
15826 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15827 errmsg("table \"%s\" has different type for column \"%s\"",
15828 RelationGetRelationName(rel
), type_attname
)));
15830 ReleaseTupleDesc(typeTupleDesc
);
15832 /* Any remaining columns at the end of the table had better be dropped. */
15833 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
15835 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
15838 if (!table_attr
->attisdropped
)
15840 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15841 errmsg("table has extra column \"%s\"",
15842 NameStr(table_attr
->attname
))));
15845 /* If the table was already typed, drop the existing dependency. */
15846 if (rel
->rd_rel
->reloftype
)
15847 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
15848 DEPENDENCY_NORMAL
);
15850 /* Record a dependency on the new type. */
15851 tableobj
.classId
= RelationRelationId
;
15852 tableobj
.objectId
= relid
;
15853 tableobj
.objectSubId
= 0;
15854 typeobj
.classId
= TypeRelationId
;
15855 typeobj
.objectId
= typeid;
15856 typeobj
.objectSubId
= 0;
15857 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
15859 /* Update pg_class.reloftype */
15860 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
15861 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
15862 if (!HeapTupleIsValid(classtuple
))
15863 elog(ERROR
, "cache lookup failed for relation %u", relid
);
15864 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
15865 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
15867 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
15869 heap_freetuple(classtuple
);
15870 table_close(relationRelation
, RowExclusiveLock
);
15872 ReleaseSysCache(typetuple
);
15878 * ALTER TABLE NOT OF
15880 * Detach a typed table from its originating type. Just clear reloftype and
15881 * remove the dependency.
15884 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
15886 Oid relid
= RelationGetRelid(rel
);
15887 Relation relationRelation
;
15890 if (!OidIsValid(rel
->rd_rel
->reloftype
))
15892 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15893 errmsg("\"%s\" is not a typed table",
15894 RelationGetRelationName(rel
))));
15897 * We don't bother to check ownership of the type --- ownership of the
15898 * table is presumed enough rights. No lock required on the type, either.
15901 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
15902 DEPENDENCY_NORMAL
);
15904 /* Clear pg_class.reloftype */
15905 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
15906 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
15907 if (!HeapTupleIsValid(tuple
))
15908 elog(ERROR
, "cache lookup failed for relation %u", relid
);
15909 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
15910 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
15912 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
15914 heap_freetuple(tuple
);
15915 table_close(relationRelation
, RowExclusiveLock
);
15919 * relation_mark_replica_identity: Update a table's replica identity
15921 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
15922 * index. Otherwise, it must be InvalidOid.
15924 * Caller had better hold an exclusive lock on the relation, as the results
15925 * of running two of these concurrently wouldn't be pretty.
15928 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
15933 HeapTuple pg_class_tuple
;
15934 HeapTuple pg_index_tuple
;
15935 Form_pg_class pg_class_form
;
15936 Form_pg_index pg_index_form
;
15940 * Check whether relreplident has changed, and update it if so.
15942 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
15943 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
15944 ObjectIdGetDatum(RelationGetRelid(rel
)));
15945 if (!HeapTupleIsValid(pg_class_tuple
))
15946 elog(ERROR
, "cache lookup failed for relation \"%s\"",
15947 RelationGetRelationName(rel
));
15948 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
15949 if (pg_class_form
->relreplident
!= ri_type
)
15951 pg_class_form
->relreplident
= ri_type
;
15952 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
15954 table_close(pg_class
, RowExclusiveLock
);
15955 heap_freetuple(pg_class_tuple
);
15958 * Update the per-index indisreplident flags correctly.
15960 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
15961 foreach(index
, RelationGetIndexList(rel
))
15963 Oid thisIndexOid
= lfirst_oid(index
);
15964 bool dirty
= false;
15966 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
15967 ObjectIdGetDatum(thisIndexOid
));
15968 if (!HeapTupleIsValid(pg_index_tuple
))
15969 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
15970 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
15972 if (thisIndexOid
== indexOid
)
15974 /* Set the bit if not already set. */
15975 if (!pg_index_form
->indisreplident
)
15978 pg_index_form
->indisreplident
= true;
15983 /* Unset the bit if set. */
15984 if (pg_index_form
->indisreplident
)
15987 pg_index_form
->indisreplident
= false;
15993 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
15994 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
15995 InvalidOid
, is_internal
);
15998 * Invalidate the relcache for the table, so that after we commit
15999 * all sessions will refresh the table's replica identity index
16000 * before attempting any UPDATE or DELETE on the table. (If we
16001 * changed the table's pg_class row above, then a relcache inval
16002 * is already queued due to that; but we might not have.)
16004 CacheInvalidateRelcache(rel
);
16006 heap_freetuple(pg_index_tuple
);
16009 table_close(pg_index
, RowExclusiveLock
);
16013 * ALTER TABLE <name> REPLICA IDENTITY ...
16016 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
16022 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
16024 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16027 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
16029 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16032 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
16034 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16037 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
16039 /* fallthrough */ ;
16042 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
16044 /* Check that the index exists */
16045 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
16046 if (!OidIsValid(indexOid
))
16048 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16049 errmsg("index \"%s\" for table \"%s\" does not exist",
16050 stmt
->name
, RelationGetRelationName(rel
))));
16052 indexRel
= index_open(indexOid
, ShareLock
);
16054 /* Check that the index is on the relation we're altering. */
16055 if (indexRel
->rd_index
== NULL
||
16056 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
16058 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16059 errmsg("\"%s\" is not an index for table \"%s\"",
16060 RelationGetRelationName(indexRel
),
16061 RelationGetRelationName(rel
))));
16062 /* The AM must support uniqueness, and the index must in fact be unique. */
16063 if (!indexRel
->rd_indam
->amcanunique
||
16064 !indexRel
->rd_index
->indisunique
)
16066 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16067 errmsg("cannot use non-unique index \"%s\" as replica identity",
16068 RelationGetRelationName(indexRel
))));
16069 /* Deferred indexes are not guaranteed to be always unique. */
16070 if (!indexRel
->rd_index
->indimmediate
)
16072 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16073 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16074 RelationGetRelationName(indexRel
))));
16075 /* Expression indexes aren't supported. */
16076 if (RelationGetIndexExpressions(indexRel
) != NIL
)
16078 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16079 errmsg("cannot use expression index \"%s\" as replica identity",
16080 RelationGetRelationName(indexRel
))));
16081 /* Predicate indexes aren't supported. */
16082 if (RelationGetIndexPredicate(indexRel
) != NIL
)
16084 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16085 errmsg("cannot use partial index \"%s\" as replica identity",
16086 RelationGetRelationName(indexRel
))));
16088 /* Check index for nullable columns. */
16089 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
16091 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
16092 Form_pg_attribute attr
;
16095 * Reject any other system columns. (Going forward, we'll disallow
16096 * indexes containing such columns in the first place, but they might
16097 * exist in older branches.)
16101 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
16102 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16103 RelationGetRelationName(indexRel
), attno
)));
16105 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
16106 if (!attr
->attnotnull
)
16108 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16109 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16110 RelationGetRelationName(indexRel
),
16111 NameStr(attr
->attname
))));
16114 /* This index is suitable for use as a replica identity. Mark it. */
16115 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
16117 index_close(indexRel
, NoLock
);
16121 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16124 ATExecSetRowSecurity(Relation rel
, bool rls
)
16130 relid
= RelationGetRelid(rel
);
16132 /* Pull the record for this relation and update it */
16133 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16135 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16137 if (!HeapTupleIsValid(tuple
))
16138 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16140 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
16141 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16143 InvokeObjectPostAlterHook(RelationRelationId
,
16144 RelationGetRelid(rel
), 0);
16146 table_close(pg_class
, RowExclusiveLock
);
16147 heap_freetuple(tuple
);
16151 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16154 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
16160 relid
= RelationGetRelid(rel
);
16162 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16164 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16166 if (!HeapTupleIsValid(tuple
))
16167 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16169 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
16170 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16172 InvokeObjectPostAlterHook(RelationRelationId
,
16173 RelationGetRelid(rel
), 0);
16175 table_close(pg_class
, RowExclusiveLock
);
16176 heap_freetuple(tuple
);
16180 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16183 ATExecGenericOptions(Relation rel
, List
*options
)
16186 ForeignServer
*server
;
16187 ForeignDataWrapper
*fdw
;
16190 Datum repl_val
[Natts_pg_foreign_table
];
16191 bool repl_null
[Natts_pg_foreign_table
];
16192 bool repl_repl
[Natts_pg_foreign_table
];
16194 Form_pg_foreign_table tableform
;
16196 if (options
== NIL
)
16199 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
16201 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
16202 ObjectIdGetDatum(rel
->rd_id
));
16203 if (!HeapTupleIsValid(tuple
))
16205 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16206 errmsg("foreign table \"%s\" does not exist",
16207 RelationGetRelationName(rel
))));
16208 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
16209 server
= GetForeignServer(tableform
->ftserver
);
16210 fdw
= GetForeignDataWrapper(server
->fdwid
);
16212 memset(repl_val
, 0, sizeof(repl_val
));
16213 memset(repl_null
, false, sizeof(repl_null
));
16214 memset(repl_repl
, false, sizeof(repl_repl
));
16216 /* Extract the current options */
16217 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
16219 Anum_pg_foreign_table_ftoptions
,
16222 datum
= PointerGetDatum(NULL
);
16224 /* Transform the options */
16225 datum
= transformGenericOptions(ForeignTableRelationId
,
16228 fdw
->fdwvalidator
);
16230 if (PointerIsValid(DatumGetPointer(datum
)))
16231 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
16233 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16235 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16237 /* Everything looks good - update the tuple */
16239 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
16240 repl_val
, repl_null
, repl_repl
);
16242 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
16245 * Invalidate relcache so that all sessions will refresh any cached plans
16246 * that might depend on the old options.
16248 CacheInvalidateRelcache(rel
);
16250 InvokeObjectPostAlterHook(ForeignTableRelationId
,
16251 RelationGetRelid(rel
), 0);
16253 table_close(ftrel
, RowExclusiveLock
);
16255 heap_freetuple(tuple
);
16259 * ALTER TABLE ALTER COLUMN SET COMPRESSION
16261 * Return value is the address of the modified column
16263 static ObjectAddress
16264 ATExecSetCompression(Relation rel
,
16265 const char *column
,
16271 Form_pg_attribute atttableform
;
16275 ObjectAddress address
;
16277 compression
= strVal(newValue
);
16279 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
16281 /* copy the cache entry so we can scribble on it below */
16282 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
16283 if (!HeapTupleIsValid(tuple
))
16285 (errcode(ERRCODE_UNDEFINED_COLUMN
),
16286 errmsg("column \"%s\" of relation \"%s\" does not exist",
16287 column
, RelationGetRelationName(rel
))));
16289 /* prevent them from altering a system attribute */
16290 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
16291 attnum
= atttableform
->attnum
;
16294 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16295 errmsg("cannot alter system column \"%s\"", column
)));
16298 * Check that column type is compressible, then get the attribute
16299 * compression method code
16301 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
16303 /* update pg_attribute entry */
16304 atttableform
->attcompression
= cmethod
;
16305 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
16307 InvokeObjectPostAlterHook(RelationRelationId
,
16308 RelationGetRelid(rel
),
16312 * Apply the change to indexes as well (only for simple index columns,
16313 * matching behavior of index.c ConstructTupleDescriptor()).
16315 SetIndexStorageProperties(rel
, attrel
, attnum
,
16320 heap_freetuple(tuple
);
16322 table_close(attrel
, RowExclusiveLock
);
16324 /* make changes visible */
16325 CommandCounterIncrement();
16327 ObjectAddressSubSet(address
, RelationRelationId
,
16328 RelationGetRelid(rel
), attnum
);
16334 * Preparation phase for SET LOGGED/UNLOGGED
16336 * This verifies that we're not trying to change a temp table. Also,
16337 * existing foreign key constraints are checked to avoid ending up with
16338 * permanent tables referencing unlogged tables.
16340 * Return value is false if the operation is a no-op (in which case the
16341 * checks are skipped), otherwise true.
16344 ATPrepChangePersistence(Relation rel
, bool toLogged
)
16346 Relation pg_constraint
;
16349 ScanKeyData skey
[1];
16352 * Disallow changing status for a temp table. Also verify whether we can
16353 * get away with doing nothing; in such cases we don't need to run the
16354 * checks below, either.
16356 switch (rel
->rd_rel
->relpersistence
)
16358 case RELPERSISTENCE_TEMP
:
16360 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
16361 errmsg("cannot change logged status of table \"%s\" because it is temporary",
16362 RelationGetRelationName(rel
)),
16365 case RELPERSISTENCE_PERMANENT
:
16367 /* nothing to do */
16370 case RELPERSISTENCE_UNLOGGED
:
16372 /* nothing to do */
16378 * Check that the table is not part of any publication when changing to
16379 * UNLOGGED, as UNLOGGED tables can't be published.
16382 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
16384 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16385 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16386 RelationGetRelationName(rel
)),
16387 errdetail("Unlogged relations cannot be replicated.")));
16390 * Check existing foreign key constraints to preserve the invariant that
16391 * permanent tables cannot reference unlogged ones. Self-referencing
16392 * foreign keys can safely be ignored.
16394 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
16397 * Scan conrelid if changing to permanent, else confrelid. This also
16398 * determines whether a useful index exists.
16400 ScanKeyInit(&skey
[0],
16401 toLogged
? Anum_pg_constraint_conrelid
:
16402 Anum_pg_constraint_confrelid
,
16403 BTEqualStrategyNumber
, F_OIDEQ
,
16404 ObjectIdGetDatum(RelationGetRelid(rel
)));
16405 scan
= systable_beginscan(pg_constraint
,
16406 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
16407 true, NULL
, 1, skey
);
16409 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
16411 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
16413 if (con
->contype
== CONSTRAINT_FOREIGN
)
16416 Relation foreignrel
;
16418 /* the opposite end of what we used as scankey */
16419 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
16421 /* ignore if self-referencing */
16422 if (RelationGetRelid(rel
) == foreignrelid
)
16425 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
16429 if (!RelationIsPermanent(foreignrel
))
16431 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
16432 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
16433 RelationGetRelationName(rel
),
16434 RelationGetRelationName(foreignrel
)),
16435 errtableconstraint(rel
, NameStr(con
->conname
))));
16439 if (RelationIsPermanent(foreignrel
))
16441 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
16442 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
16443 RelationGetRelationName(rel
),
16444 RelationGetRelationName(foreignrel
)),
16445 errtableconstraint(rel
, NameStr(con
->conname
))));
16448 relation_close(foreignrel
, AccessShareLock
);
16452 systable_endscan(scan
);
16454 table_close(pg_constraint
, AccessShareLock
);
16460 * Execute ALTER TABLE SET SCHEMA
16463 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
16470 ObjectAddresses
*objsMoved
;
16471 ObjectAddress myself
;
16473 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
16474 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
16475 RangeVarCallbackForAlterRelation
,
16478 if (!OidIsValid(relid
))
16481 (errmsg("relation \"%s\" does not exist, skipping",
16482 stmt
->relation
->relname
)));
16483 return InvalidObjectAddress
;
16486 rel
= relation_open(relid
, NoLock
);
16488 oldNspOid
= RelationGetNamespace(rel
);
16490 /* If it's an owned sequence, disallow moving it by itself. */
16491 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
16496 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
16497 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
16499 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16500 errmsg("cannot move an owned sequence into another schema"),
16501 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16502 RelationGetRelationName(rel
),
16503 get_rel_name(tableId
))));
16506 /* Get and lock schema OID and check its permissions. */
16507 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
16508 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
16510 /* common checks on switching namespaces */
16511 CheckSetNamespace(oldNspOid
, nspOid
);
16513 objsMoved
= new_object_addresses();
16514 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
16515 free_object_addresses(objsMoved
);
16517 ObjectAddressSet(myself
, RelationRelationId
, relid
);
16520 *oldschema
= oldNspOid
;
16522 /* close rel, but keep lock until commit */
16523 relation_close(rel
, NoLock
);
16529 * The guts of relocating a table or materialized view to another namespace:
16530 * besides moving the relation itself, its dependent objects are relocated to
16534 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
16535 ObjectAddresses
*objsMoved
)
16539 Assert(objsMoved
!= NULL
);
16541 /* OK, modify the pg_class row and pg_depend entry */
16542 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
16544 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
16545 nspOid
, true, objsMoved
);
16547 /* Fix the table's row type too, if it has one */
16548 if (OidIsValid(rel
->rd_rel
->reltype
))
16549 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
,
16550 nspOid
, false, false, objsMoved
);
16552 /* Fix other dependent stuff */
16553 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
||
16554 rel
->rd_rel
->relkind
== RELKIND_MATVIEW
||
16555 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16557 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
16558 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
16559 objsMoved
, AccessExclusiveLock
);
16560 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
16564 table_close(classRel
, RowExclusiveLock
);
16568 * The guts of relocating a relation to another namespace: fix the pg_class
16569 * entry, and the pg_depend entry if any. Caller must already have
16570 * opened and write-locked pg_class.
16573 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
16574 Oid oldNspOid
, Oid newNspOid
,
16575 bool hasDependEntry
,
16576 ObjectAddresses
*objsMoved
)
16578 HeapTuple classTup
;
16579 Form_pg_class classForm
;
16580 ObjectAddress thisobj
;
16581 bool already_done
= false;
16583 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relOid
));
16584 if (!HeapTupleIsValid(classTup
))
16585 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
16586 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
16588 Assert(classForm
->relnamespace
== oldNspOid
);
16590 thisobj
.classId
= RelationRelationId
;
16591 thisobj
.objectId
= relOid
;
16592 thisobj
.objectSubId
= 0;
16595 * If the object has already been moved, don't move it again. If it's
16596 * already in the right place, don't move it, but still fire the object
16599 already_done
= object_address_present(&thisobj
, objsMoved
);
16600 if (!already_done
&& oldNspOid
!= newNspOid
)
16602 /* check for duplicate name (more friendly than unique-index failure) */
16603 if (get_relname_relid(NameStr(classForm
->relname
),
16604 newNspOid
) != InvalidOid
)
16606 (errcode(ERRCODE_DUPLICATE_TABLE
),
16607 errmsg("relation \"%s\" already exists in schema \"%s\"",
16608 NameStr(classForm
->relname
),
16609 get_namespace_name(newNspOid
))));
16611 /* classTup is a copy, so OK to scribble on */
16612 classForm
->relnamespace
= newNspOid
;
16614 CatalogTupleUpdate(classRel
, &classTup
->t_self
, classTup
);
16616 /* Update dependency on schema if caller said so */
16617 if (hasDependEntry
&&
16618 changeDependencyFor(RelationRelationId
,
16620 NamespaceRelationId
,
16623 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
16624 NameStr(classForm
->relname
));
16628 add_exact_object_address(&thisobj
, objsMoved
);
16630 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
16633 heap_freetuple(classTup
);
16637 * Move all indexes for the specified relation to another namespace.
16639 * Note: we assume adequate permission checking was done by the caller,
16640 * and that the caller has a suitable lock on the owning relation.
16643 AlterIndexNamespaces(Relation classRel
, Relation rel
,
16644 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
16649 indexList
= RelationGetIndexList(rel
);
16651 foreach(l
, indexList
)
16653 Oid indexOid
= lfirst_oid(l
);
16654 ObjectAddress thisobj
;
16656 thisobj
.classId
= RelationRelationId
;
16657 thisobj
.objectId
= indexOid
;
16658 thisobj
.objectSubId
= 0;
16661 * Note: currently, the index will not have its own dependency on the
16662 * namespace, so we don't need to do changeDependencyFor(). There's no
16663 * row type in pg_type, either.
16665 * XXX this objsMoved test may be pointless -- surely we have a single
16666 * dependency link from a relation to each index?
16668 if (!object_address_present(&thisobj
, objsMoved
))
16670 AlterRelationNamespaceInternal(classRel
, indexOid
,
16671 oldNspOid
, newNspOid
,
16673 add_exact_object_address(&thisobj
, objsMoved
);
16677 list_free(indexList
);
16681 * Move all identity and SERIAL-column sequences of the specified relation to another
16684 * Note: we assume adequate permission checking was done by the caller,
16685 * and that the caller has a suitable lock on the owning relation.
16688 AlterSeqNamespaces(Relation classRel
, Relation rel
,
16689 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
16694 ScanKeyData key
[2];
16698 * SERIAL sequences are those having an auto dependency on one of the
16699 * table's columns (we don't care *which* column, exactly).
16701 depRel
= table_open(DependRelationId
, AccessShareLock
);
16703 ScanKeyInit(&key
[0],
16704 Anum_pg_depend_refclassid
,
16705 BTEqualStrategyNumber
, F_OIDEQ
,
16706 ObjectIdGetDatum(RelationRelationId
));
16707 ScanKeyInit(&key
[1],
16708 Anum_pg_depend_refobjid
,
16709 BTEqualStrategyNumber
, F_OIDEQ
,
16710 ObjectIdGetDatum(RelationGetRelid(rel
)));
16711 /* we leave refobjsubid unspecified */
16713 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
16716 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
16718 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
16721 /* skip dependencies other than auto dependencies on columns */
16722 if (depForm
->refobjsubid
== 0 ||
16723 depForm
->classid
!= RelationRelationId
||
16724 depForm
->objsubid
!= 0 ||
16725 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
16728 /* Use relation_open just in case it's an index */
16729 seqRel
= relation_open(depForm
->objid
, lockmode
);
16731 /* skip non-sequence relations */
16732 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
16734 /* No need to keep the lock */
16735 relation_close(seqRel
, lockmode
);
16739 /* Fix the pg_class and pg_depend entries */
16740 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
16741 oldNspOid
, newNspOid
,
16745 * Sequences used to have entries in pg_type, but no longer do. If we
16746 * ever re-instate that, we'll need to move the pg_type entry to the
16747 * new namespace, too (using AlterTypeNamespaceInternal).
16749 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
16751 /* Now we can close it. Keep the lock till end of transaction. */
16752 relation_close(seqRel
, NoLock
);
16755 systable_endscan(scan
);
16757 relation_close(depRel
, AccessShareLock
);
16762 * This code supports
16763 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
16765 * Because we only support this for TEMP tables, it's sufficient to remember
16766 * the state in a backend-local data structure.
16770 * Register a newly-created relation's ON COMMIT action.
16773 register_on_commit_action(Oid relid
, OnCommitAction action
)
16776 MemoryContext oldcxt
;
16779 * We needn't bother registering the relation unless there is an ON COMMIT
16780 * action we need to take.
16782 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
16785 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
16787 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
16789 oc
->oncommit
= action
;
16790 oc
->creating_subid
= GetCurrentSubTransactionId();
16791 oc
->deleting_subid
= InvalidSubTransactionId
;
16794 * We use lcons() here so that ON COMMIT actions are processed in reverse
16795 * order of registration. That might not be essential but it seems
16798 on_commits
= lcons(oc
, on_commits
);
16800 MemoryContextSwitchTo(oldcxt
);
16804 * Unregister any ON COMMIT action when a relation is deleted.
16806 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
16809 remove_on_commit_action(Oid relid
)
16813 foreach(l
, on_commits
)
16815 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
16817 if (oc
->relid
== relid
)
16819 oc
->deleting_subid
= GetCurrentSubTransactionId();
16826 * Perform ON COMMIT actions.
16828 * This is invoked just before actually committing, since it's possible
16829 * to encounter errors.
16832 PreCommit_on_commit_actions(void)
16835 List
*oids_to_truncate
= NIL
;
16836 List
*oids_to_drop
= NIL
;
16838 foreach(l
, on_commits
)
16840 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
16842 /* Ignore entry if already dropped in this xact */
16843 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
16846 switch (oc
->oncommit
)
16848 case ONCOMMIT_NOOP
:
16849 case ONCOMMIT_PRESERVE_ROWS
:
16850 /* Do nothing (there shouldn't be such entries, actually) */
16852 case ONCOMMIT_DELETE_ROWS
:
16855 * If this transaction hasn't accessed any temporary
16856 * relations, we can skip truncating ON COMMIT DELETE ROWS
16857 * tables, as they must still be empty.
16859 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
16860 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
16862 case ONCOMMIT_DROP
:
16863 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
16869 * Truncate relations before dropping so that all dependencies between
16870 * relations are removed after they are worked on. Doing it like this
16871 * might be a waste as it is possible that a relation being truncated will
16872 * be dropped anyway due to its parent being dropped, but this makes the
16873 * code more robust because of not having to re-check that the relation
16874 * exists at truncation time.
16876 if (oids_to_truncate
!= NIL
)
16877 heap_truncate(oids_to_truncate
);
16879 if (oids_to_drop
!= NIL
)
16881 ObjectAddresses
*targetObjects
= new_object_addresses();
16883 foreach(l
, oids_to_drop
)
16885 ObjectAddress object
;
16887 object
.classId
= RelationRelationId
;
16888 object
.objectId
= lfirst_oid(l
);
16889 object
.objectSubId
= 0;
16891 Assert(!object_address_present(&object
, targetObjects
));
16893 add_exact_object_address(&object
, targetObjects
);
16897 * Since this is an automatic drop, rather than one directly initiated
16898 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
16900 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
16901 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
16903 #ifdef USE_ASSERT_CHECKING
16906 * Note that table deletion will call remove_on_commit_action, so the
16907 * entry should get marked as deleted.
16909 foreach(l
, on_commits
)
16911 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
16913 if (oc
->oncommit
!= ONCOMMIT_DROP
)
16916 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
16923 * Post-commit or post-abort cleanup for ON COMMIT management.
16925 * All we do here is remove no-longer-needed OnCommitItem entries.
16927 * During commit, remove entries that were deleted during this transaction;
16928 * during abort, remove those created during this transaction.
16931 AtEOXact_on_commit_actions(bool isCommit
)
16933 ListCell
*cur_item
;
16935 foreach(cur_item
, on_commits
)
16937 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
16939 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
16940 oc
->creating_subid
!= InvalidSubTransactionId
)
16942 /* cur_item must be removed */
16943 on_commits
= foreach_delete_current(on_commits
, cur_item
);
16948 /* cur_item must be preserved */
16949 oc
->creating_subid
= InvalidSubTransactionId
;
16950 oc
->deleting_subid
= InvalidSubTransactionId
;
16956 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
16958 * During subabort, we can immediately remove entries created during this
16959 * subtransaction. During subcommit, just relabel entries marked during
16960 * this subtransaction as being the parent's responsibility.
16963 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
16964 SubTransactionId parentSubid
)
16966 ListCell
*cur_item
;
16968 foreach(cur_item
, on_commits
)
16970 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
16972 if (!isCommit
&& oc
->creating_subid
== mySubid
)
16974 /* cur_item must be removed */
16975 on_commits
= foreach_delete_current(on_commits
, cur_item
);
16980 /* cur_item must be preserved */
16981 if (oc
->creating_subid
== mySubid
)
16982 oc
->creating_subid
= parentSubid
;
16983 if (oc
->deleting_subid
== mySubid
)
16984 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
16990 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
16991 * the relation to be locked only if (1) it's a plain or partitioned table,
16992 * materialized view, or TOAST table and (2) the current user is the owner (or
16993 * the superuser). This meets the permission-checking needs of CLUSTER,
16994 * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it
16995 * can be used by all.
16998 RangeVarCallbackOwnsTable(const RangeVar
*relation
,
16999 Oid relId
, Oid oldRelId
, void *arg
)
17003 /* Nothing to do if the relation was not found. */
17004 if (!OidIsValid(relId
))
17008 * If the relation does exist, check whether it's an index. But note that
17009 * the relation might have been dropped between the time we did the name
17010 * lookup and now. In that case, there's nothing to do.
17012 relkind
= get_rel_relkind(relId
);
17015 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
17016 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
17018 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17019 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
17021 /* Check permissions */
17022 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
17023 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)), relation
->relname
);
17027 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17030 RangeVarCallbackForTruncate(const RangeVar
*relation
,
17031 Oid relId
, Oid oldRelId
, void *arg
)
17035 /* Nothing to do if the relation was not found. */
17036 if (!OidIsValid(relId
))
17039 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17040 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17041 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17043 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17044 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17046 ReleaseSysCache(tuple
);
17050 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17051 * the owner of the relation, or superuser.
17054 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
17055 Oid relId
, Oid oldRelId
, void *arg
)
17059 /* Nothing to do if the relation was not found. */
17060 if (!OidIsValid(relId
))
17063 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17064 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17065 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17067 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
17068 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
17069 relation
->relname
);
17071 if (!allowSystemTableMods
&&
17072 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
17074 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17075 errmsg("permission denied: \"%s\" is a system catalog",
17076 relation
->relname
)));
17078 ReleaseSysCache(tuple
);
17082 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17086 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
17089 Node
*stmt
= (Node
*) arg
;
17090 ObjectType reltype
;
17092 Form_pg_class classform
;
17093 AclResult aclresult
;
17096 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
17097 if (!HeapTupleIsValid(tuple
))
17098 return; /* concurrently dropped */
17099 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
17100 relkind
= classform
->relkind
;
17102 /* Must own relation. */
17103 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
17104 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
17106 /* No system table modifications unless explicitly allowed. */
17107 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
17109 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17110 errmsg("permission denied: \"%s\" is a system catalog",
17114 * Extract the specified relation type from the statement parse tree.
17116 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17117 * have CREATE rights on the containing namespace.
17119 if (IsA(stmt
, RenameStmt
))
17121 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
17122 GetUserId(), ACL_CREATE
);
17123 if (aclresult
!= ACLCHECK_OK
)
17124 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
17125 get_namespace_name(classform
->relnamespace
));
17126 reltype
= ((RenameStmt
*) stmt
)->renameType
;
17128 else if (IsA(stmt
, AlterObjectSchemaStmt
))
17129 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
17131 else if (IsA(stmt
, AlterTableStmt
))
17132 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
17135 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
17136 reltype
= OBJECT_TABLE
; /* placate compiler */
17140 * For compatibility with prior releases, we allow ALTER TABLE to be used
17141 * with most other types of relations (but not composite types). We allow
17142 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17143 * otherwise. Otherwise, the user must select the correct form of the
17144 * command for the relation at issue.
17146 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
17148 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17149 errmsg("\"%s\" is not a sequence", rv
->relname
)));
17151 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
17153 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17154 errmsg("\"%s\" is not a view", rv
->relname
)));
17156 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
17158 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17159 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
17161 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
17163 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17164 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
17166 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
17168 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17169 errmsg("\"%s\" is not a composite type", rv
->relname
)));
17171 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
17172 relkind
!= RELKIND_PARTITIONED_INDEX
17173 && !IsA(stmt
, RenameStmt
))
17175 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17176 errmsg("\"%s\" is not an index", rv
->relname
)));
17179 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17182 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
17184 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17185 errmsg("\"%s\" is a composite type", rv
->relname
),
17186 errhint("Use ALTER TYPE instead.")));
17189 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17190 * to a different schema, such as indexes and TOAST tables.
17192 if (IsA(stmt
, AlterObjectSchemaStmt
))
17194 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
17196 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17197 errmsg("cannot change schema of index \"%s\"",
17199 errhint("Change the schema of the table instead.")));
17200 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
17202 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17203 errmsg("cannot change schema of composite type \"%s\"",
17205 errhint("Use ALTER TYPE instead.")));
17206 else if (relkind
== RELKIND_TOASTVALUE
)
17208 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17209 errmsg("cannot change schema of TOAST table \"%s\"",
17211 errhint("Change the schema of the table instead.")));
17214 ReleaseSysCache(tuple
);
17218 * Transform any expressions present in the partition key
17220 * Returns a transformed PartitionSpec.
17222 static PartitionSpec
*
17223 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
17225 PartitionSpec
*newspec
;
17226 ParseState
*pstate
;
17227 ParseNamespaceItem
*nsitem
;
17230 newspec
= makeNode(PartitionSpec
);
17232 newspec
->strategy
= partspec
->strategy
;
17233 newspec
->partParams
= NIL
;
17234 newspec
->location
= partspec
->location
;
17236 /* Check valid number of columns for strategy */
17237 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
17238 list_length(partspec
->partParams
) != 1)
17240 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17241 errmsg("cannot use \"list\" partition strategy with more than one column")));
17244 * Create a dummy ParseState and insert the target relation as its sole
17245 * rangetable entry. We need a ParseState for transformExpr.
17247 pstate
= make_parsestate(NULL
);
17248 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
17249 NULL
, false, true);
17250 addNSItemToQuery(pstate
, nsitem
, true, true, true);
17252 /* take care of any partition expressions */
17253 foreach(l
, partspec
->partParams
)
17255 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
17259 /* Copy, to avoid scribbling on the input */
17260 pelem
= copyObject(pelem
);
17262 /* Now do parse transformation of the expression */
17263 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
17264 EXPR_KIND_PARTITION_EXPRESSION
);
17266 /* we have to fix its collations too */
17267 assign_expr_collations(pstate
, pelem
->expr
);
17270 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
17277 * Compute per-partition-column information from a list of PartitionElems.
17278 * Expressions in the PartitionElems must be parse-analyzed already.
17281 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
17282 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
17283 PartitionStrategy strategy
)
17290 foreach(lc
, partParams
)
17292 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
17296 if (pelem
->name
!= NULL
)
17298 /* Simple attribute reference */
17299 HeapTuple atttuple
;
17300 Form_pg_attribute attform
;
17302 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
17304 if (!HeapTupleIsValid(atttuple
))
17306 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17307 errmsg("column \"%s\" named in partition key does not exist",
17309 parser_errposition(pstate
, pelem
->location
)));
17310 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
17312 if (attform
->attnum
<= 0)
17314 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17315 errmsg("cannot use system column \"%s\" in partition key",
17317 parser_errposition(pstate
, pelem
->location
)));
17320 * Generated columns cannot work: They are computed after BEFORE
17321 * triggers, but partition routing is done before all triggers.
17323 if (attform
->attgenerated
)
17325 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17326 errmsg("cannot use generated column in partition key"),
17327 errdetail("Column \"%s\" is a generated column.",
17329 parser_errposition(pstate
, pelem
->location
)));
17331 partattrs
[attn
] = attform
->attnum
;
17332 atttype
= attform
->atttypid
;
17333 attcollation
= attform
->attcollation
;
17334 ReleaseSysCache(atttuple
);
17339 Node
*expr
= pelem
->expr
;
17340 char partattname
[16];
17342 Assert(expr
!= NULL
);
17343 atttype
= exprType(expr
);
17344 attcollation
= exprCollation(expr
);
17347 * The expression must be of a storable type (e.g., not RECORD).
17348 * The test is the same as for whether a table column is of a safe
17349 * type (which is why we needn't check for the non-expression
17352 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
17353 CheckAttributeType(partattname
,
17354 atttype
, attcollation
,
17355 NIL
, CHKATYPE_IS_PARTKEY
);
17358 * Strip any top-level COLLATE clause. This ensures that we treat
17359 * "x COLLATE y" and "(x COLLATE y)" alike.
17361 while (IsA(expr
, CollateExpr
))
17362 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
17364 if (IsA(expr
, Var
) &&
17365 ((Var
*) expr
)->varattno
> 0)
17368 * User wrote "(column)" or "(column COLLATE something)".
17369 * Treat it like simple attribute anyway.
17371 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
17375 Bitmapset
*expr_attrs
= NULL
;
17378 partattrs
[attn
] = 0; /* marks the column as expression */
17379 *partexprs
= lappend(*partexprs
, expr
);
17382 * Try to simplify the expression before checking for
17383 * mutability. The main practical value of doing it in this
17384 * order is that an inline-able SQL-language function will be
17385 * accepted if its expansion is immutable, whether or not the
17386 * function itself is marked immutable.
17388 * Note that expression_planner does not change the passed in
17389 * expression destructively and we have already saved the
17390 * expression to be stored into the catalog above.
17392 expr
= (Node
*) expression_planner((Expr
*) expr
);
17395 * Partition expression cannot contain mutable functions,
17396 * because a given row must always map to the same partition
17397 * as long as there is no change in the partition boundary
17400 if (contain_mutable_functions(expr
))
17402 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17403 errmsg("functions in partition key expression must be marked IMMUTABLE")));
17406 * transformPartitionSpec() should have already rejected
17407 * subqueries, aggregates, window functions, and SRFs, based
17408 * on the EXPR_KIND_ for partition expressions.
17412 * Cannot allow system column references, since that would
17413 * make partition routing impossible: their values won't be
17414 * known yet when we need to do that.
17416 pull_varattnos(expr
, 1, &expr_attrs
);
17417 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
17419 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
17422 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17423 errmsg("partition key expressions cannot contain system column references")));
17427 * Generated columns cannot work: They are computed after
17428 * BEFORE triggers, but partition routing is done before all
17432 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
17434 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
17437 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
17439 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17440 errmsg("cannot use generated column in partition key"),
17441 errdetail("Column \"%s\" is a generated column.",
17442 get_attname(RelationGetRelid(rel
), attno
, false)),
17443 parser_errposition(pstate
, pelem
->location
)));
17447 * While it is not exactly *wrong* for a partition expression
17448 * to be a constant, it seems better to reject such keys.
17450 if (IsA(expr
, Const
))
17452 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17453 errmsg("cannot use constant expression as partition key")));
17458 * Apply collation override if any
17460 if (pelem
->collation
)
17461 attcollation
= get_collation_oid(pelem
->collation
, false);
17464 * Check we have a collation iff it's a collatable type. The only
17465 * expected failures here are (1) COLLATE applied to a noncollatable
17466 * type, or (2) partition expression had an unresolved collation. But
17467 * we might as well code this to be a complete consistency check.
17469 if (type_is_collatable(atttype
))
17471 if (!OidIsValid(attcollation
))
17473 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
17474 errmsg("could not determine which collation to use for partition expression"),
17475 errhint("Use the COLLATE clause to set the collation explicitly.")));
17479 if (OidIsValid(attcollation
))
17481 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17482 errmsg("collations are not supported by type %s",
17483 format_type_be(atttype
))));
17486 partcollation
[attn
] = attcollation
;
17489 * Identify the appropriate operator class. For list and range
17490 * partitioning, we use a btree operator class; hash partitioning uses
17491 * a hash operator class.
17493 if (strategy
== PARTITION_STRATEGY_HASH
)
17494 am_oid
= HASH_AM_OID
;
17496 am_oid
= BTREE_AM_OID
;
17498 if (!pelem
->opclass
)
17500 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
17502 if (!OidIsValid(partopclass
[attn
]))
17504 if (strategy
== PARTITION_STRATEGY_HASH
)
17506 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17507 errmsg("data type %s has no default operator class for access method \"%s\"",
17508 format_type_be(atttype
), "hash"),
17509 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
17512 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17513 errmsg("data type %s has no default operator class for access method \"%s\"",
17514 format_type_be(atttype
), "btree"),
17515 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
17519 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
17521 am_oid
== HASH_AM_OID
? "hash" : "btree",
17529 * PartConstraintImpliedByRelConstraint
17530 * Do scanrel's existing constraints imply the partition constraint?
17532 * "Existing constraints" include its check constraints and column-level
17533 * NOT NULL constraints. partConstraint describes the partition constraint,
17534 * in implicit-AND form.
17537 PartConstraintImpliedByRelConstraint(Relation scanrel
,
17538 List
*partConstraint
)
17540 List
*existConstraint
= NIL
;
17541 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
17544 if (constr
&& constr
->has_not_null
)
17546 int natts
= scanrel
->rd_att
->natts
;
17548 for (i
= 1; i
<= natts
; i
++)
17550 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
17552 if (att
->attnotnull
&& !att
->attisdropped
)
17554 NullTest
*ntest
= makeNode(NullTest
);
17556 ntest
->arg
= (Expr
*) makeVar(1,
17562 ntest
->nulltesttype
= IS_NOT_NULL
;
17565 * argisrow=false is correct even for a composite column,
17566 * because attnotnull does not represent a SQL-spec IS NOT
17567 * NULL test in such a case, just IS DISTINCT FROM NULL.
17569 ntest
->argisrow
= false;
17570 ntest
->location
= -1;
17571 existConstraint
= lappend(existConstraint
, ntest
);
17576 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
17580 * ConstraintImpliedByRelConstraint
17581 * Do scanrel's existing constraints imply the given constraint?
17583 * testConstraint is the constraint to validate. provenConstraint is a
17584 * caller-provided list of conditions which this function may assume
17585 * to be true. Both provenConstraint and testConstraint must be in
17586 * implicit-AND form, must only contain immutable clauses, and must
17587 * contain only Vars with varno = 1.
17590 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
17592 List
*existConstraint
= list_copy(provenConstraint
);
17593 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
17597 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
17598 for (i
= 0; i
< num_check
; i
++)
17603 * If this constraint hasn't been fully validated yet, we must ignore
17606 if (!constr
->check
[i
].ccvalid
)
17609 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
17612 * Run each expression through const-simplification and
17613 * canonicalization. It is necessary, because we will be comparing it
17614 * to similarly-processed partition constraint expressions, and may
17615 * fail to detect valid matches without this.
17617 cexpr
= eval_const_expressions(NULL
, cexpr
);
17618 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
17620 existConstraint
= list_concat(existConstraint
,
17621 make_ands_implicit((Expr
*) cexpr
));
17625 * Try to make the proof. Since we are comparing CHECK constraints, we
17626 * need to use weak implication, i.e., we assume existConstraint is
17627 * not-false and try to prove the same for testConstraint.
17629 * Note that predicate_implied_by assumes its first argument is known
17630 * immutable. That should always be true for both NOT NULL and partition
17631 * constraints, so we don't test it here.
17633 return predicate_implied_by(testConstraint
, existConstraint
, true);
17637 * QueuePartitionConstraintValidation
17639 * Add an entry to wqueue to have the given partition constraint validated by
17640 * Phase 3, for the given relation, and all its children.
17642 * We first verify whether the given constraint is implied by pre-existing
17643 * relation constraints; if it is, there's no need to scan the table to
17644 * validate, so don't queue in that case.
17647 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
17648 List
*partConstraint
,
17649 bool validate_default
)
17652 * Based on the table's existing constraints, determine whether or not we
17653 * may skip scanning the table.
17655 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
17657 if (!validate_default
)
17659 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
17660 RelationGetRelationName(scanrel
))));
17663 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
17664 RelationGetRelationName(scanrel
))));
17669 * Constraints proved insufficient. For plain relations, queue a
17670 * validation item now; for partitioned tables, recurse to process each
17673 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
17675 AlteredTableInfo
*tab
;
17677 /* Grab a work queue entry. */
17678 tab
= ATGetQueueEntry(wqueue
, scanrel
);
17679 Assert(tab
->partition_constraint
== NULL
);
17680 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
17681 tab
->validate_default
= validate_default
;
17683 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
17685 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
17688 for (i
= 0; i
< partdesc
->nparts
; i
++)
17691 List
*thisPartConstraint
;
17694 * This is the minimum lock we need to prevent deadlocks.
17696 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
17699 * Adjust the constraint for scanrel so that it matches this
17700 * partition's attribute numbers.
17702 thisPartConstraint
=
17703 map_partition_varattnos(partConstraint
, 1,
17704 part_rel
, scanrel
);
17706 QueuePartitionConstraintValidation(wqueue
, part_rel
,
17707 thisPartConstraint
,
17709 table_close(part_rel
, NoLock
); /* keep lock till commit */
17715 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
17717 * Return the address of the newly attached partition.
17719 static ObjectAddress
17720 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
17721 AlterTableUtilityContext
*context
)
17723 Relation attachrel
,
17725 List
*attachrel_children
;
17726 List
*partConstraint
;
17731 TupleDesc tupleDesc
;
17732 ObjectAddress address
;
17733 const char *trigger_name
;
17734 Oid defaultPartOid
;
17735 List
*partBoundConstraint
;
17736 ParseState
*pstate
= make_parsestate(NULL
);
17738 pstate
->p_sourcetext
= context
->queryString
;
17741 * We must lock the default partition if one exists, because attaching a
17742 * new partition will change its partition constraint.
17745 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
17746 if (OidIsValid(defaultPartOid
))
17747 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
17749 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
17752 * XXX I think it'd be a good idea to grab locks on all tables referenced
17753 * by FKs at this point also.
17757 * Must be owner of both parent and source table -- parent was checked by
17758 * ATSimplePermissions call in ATPrepCmd
17760 ATSimplePermissions(AT_AttachPartition
, attachrel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
17762 /* A partition can only have one parent */
17763 if (attachrel
->rd_rel
->relispartition
)
17765 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17766 errmsg("\"%s\" is already a partition",
17767 RelationGetRelationName(attachrel
))));
17769 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
17771 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17772 errmsg("cannot attach a typed table as partition")));
17775 * Table being attached should not already be part of inheritance; either
17776 * as a child table...
17778 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
17780 Anum_pg_inherits_inhrelid
,
17781 BTEqualStrategyNumber
, F_OIDEQ
,
17782 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
17783 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
17785 if (HeapTupleIsValid(systable_getnext(scan
)))
17787 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17788 errmsg("cannot attach inheritance child as partition")));
17789 systable_endscan(scan
);
17791 /* ...or as a parent table (except the case when it is partitioned) */
17793 Anum_pg_inherits_inhparent
,
17794 BTEqualStrategyNumber
, F_OIDEQ
,
17795 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
17796 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
17798 if (HeapTupleIsValid(systable_getnext(scan
)) &&
17799 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
17801 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17802 errmsg("cannot attach inheritance parent as partition")));
17803 systable_endscan(scan
);
17804 table_close(catalog
, AccessShareLock
);
17807 * Prevent circularity by seeing if rel is a partition of attachrel. (In
17808 * particular, this disallows making a rel a partition of itself.)
17810 * We do that by checking if rel is a member of the list of attachrel's
17811 * partitions provided the latter is partitioned at all. We want to avoid
17812 * having to construct this list again, so we request the strongest lock
17813 * on all partitions. We need the strongest lock, because we may decide
17814 * to scan them if we find out that the table being attached (or its leaf
17815 * partitions) may contain rows that violate the partition constraint. If
17816 * the table has a constraint that would prevent such rows, which by
17817 * definition is present in all the partitions, we need not scan the
17818 * table, nor its partitions. But we cannot risk a deadlock by taking a
17819 * weaker lock now and the stronger one only when needed.
17821 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
17822 AccessExclusiveLock
, NULL
);
17823 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
17825 (errcode(ERRCODE_DUPLICATE_TABLE
),
17826 errmsg("circular inheritance not allowed"),
17827 errdetail("\"%s\" is already a child of \"%s\".",
17828 RelationGetRelationName(rel
),
17829 RelationGetRelationName(attachrel
))));
17831 /* If the parent is permanent, so must be all of its partitions. */
17832 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
17833 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
17835 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17836 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
17837 RelationGetRelationName(rel
))));
17839 /* Temp parent cannot have a partition that is itself not a temp */
17840 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
17841 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
17843 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17844 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
17845 RelationGetRelationName(rel
))));
17847 /* If the parent is temp, it must belong to this session */
17848 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
17849 !rel
->rd_islocaltemp
)
17851 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17852 errmsg("cannot attach as partition of temporary relation of another session")));
17854 /* Ditto for the partition */
17855 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
17856 !attachrel
->rd_islocaltemp
)
17858 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17859 errmsg("cannot attach temporary relation of another session as partition")));
17861 /* Check if there are any columns in attachrel that aren't in the parent */
17862 tupleDesc
= RelationGetDescr(attachrel
);
17863 natts
= tupleDesc
->natts
;
17864 for (attno
= 1; attno
<= natts
; attno
++)
17866 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
17867 char *attributeName
= NameStr(attribute
->attname
);
17869 /* Ignore dropped */
17870 if (attribute
->attisdropped
)
17873 /* Try to find the column in parent (matching on column name) */
17874 if (!SearchSysCacheExists2(ATTNAME
,
17875 ObjectIdGetDatum(RelationGetRelid(rel
)),
17876 CStringGetDatum(attributeName
)))
17878 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17879 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
17880 RelationGetRelationName(attachrel
), attributeName
,
17881 RelationGetRelationName(rel
)),
17882 errdetail("The new partition may contain only the columns present in parent.")));
17886 * If child_rel has row-level triggers with transition tables, we
17887 * currently don't allow it to become a partition. See also prohibitions
17888 * in ATExecAddInherit() and CreateTrigger().
17890 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
17891 if (trigger_name
!= NULL
)
17893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17894 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
17895 trigger_name
, RelationGetRelationName(attachrel
)),
17896 errdetail("ROW triggers with transition tables are not supported on partitions.")));
17899 * Check that the new partition's bound is valid and does not overlap any
17900 * of existing partitions of the parent - note that it does not return on
17903 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
17904 cmd
->bound
, pstate
);
17906 /* OK to create inheritance. Rest of the checks performed there */
17907 CreateInheritance(attachrel
, rel
);
17909 /* Update the pg_class entry. */
17910 StorePartitionBound(attachrel
, rel
, cmd
->bound
);
17912 /* Ensure there exists a correct set of indexes in the partition. */
17913 AttachPartitionEnsureIndexes(rel
, attachrel
);
17916 CloneRowTriggersToPartition(rel
, attachrel
);
17919 * Clone foreign key constraints. Callee is responsible for setting up
17920 * for phase 3 constraint verification.
17922 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
17925 * Generate partition constraint from the partition bound specification.
17926 * If the parent itself is a partition, make sure to include its
17927 * constraint as well.
17929 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
17930 partConstraint
= list_concat(partBoundConstraint
,
17931 RelationGetPartitionQual(rel
));
17933 /* Skip validation if there are no constraints to validate. */
17934 if (partConstraint
)
17937 * Run the partition quals through const-simplification similar to
17938 * check constraints. We skip canonicalize_qual, though, because
17939 * partition quals should be in canonical form already.
17942 (List
*) eval_const_expressions(NULL
,
17943 (Node
*) partConstraint
);
17945 /* XXX this sure looks wrong */
17946 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
17949 * Adjust the generated constraint to match this partition's attribute
17952 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
17955 /* Validate partition constraints against the table being attached. */
17956 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
17961 * If we're attaching a partition other than the default partition and a
17962 * default one exists, then that partition's partition constraint changes,
17963 * so add an entry to the work queue to validate it, too. (We must not do
17964 * this when the partition being attached is the default one; we already
17967 if (OidIsValid(defaultPartOid
))
17969 Relation defaultrel
;
17970 List
*defPartConstraint
;
17972 Assert(!cmd
->bound
->is_default
);
17974 /* we already hold a lock on the default partition */
17975 defaultrel
= table_open(defaultPartOid
, NoLock
);
17976 defPartConstraint
=
17977 get_proposed_default_constraint(partBoundConstraint
);
17980 * Map the Vars in the constraint expression from rel's attnos to
17983 defPartConstraint
=
17984 map_partition_varattnos(defPartConstraint
,
17985 1, defaultrel
, rel
);
17986 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
17987 defPartConstraint
, true);
17989 /* keep our lock until commit. */
17990 table_close(defaultrel
, NoLock
);
17993 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
17996 * If the partition we just attached is partitioned itself, invalidate
17997 * relcache for all descendent partitions too to ensure that their
17998 * rd_partcheck expression trees are rebuilt; partitions already locked at
17999 * the beginning of this function.
18001 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18005 foreach(l
, attachrel_children
)
18007 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
18011 /* keep our lock until commit */
18012 table_close(attachrel
, NoLock
);
18018 * AttachPartitionEnsureIndexes
18019 * subroutine for ATExecAttachPartition to create/match indexes
18021 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18022 * PARTITION: every partition must have an index attached to each index on the
18023 * partitioned table.
18026 AttachPartitionEnsureIndexes(Relation rel
, Relation attachrel
)
18029 List
*attachRelIdxs
;
18030 Relation
*attachrelIdxRels
;
18031 IndexInfo
**attachInfos
;
18035 MemoryContext oldcxt
;
18037 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
18038 "AttachPartitionEnsureIndexes",
18039 ALLOCSET_DEFAULT_SIZES
);
18040 oldcxt
= MemoryContextSwitchTo(cxt
);
18042 idxes
= RelationGetIndexList(rel
);
18043 attachRelIdxs
= RelationGetIndexList(attachrel
);
18044 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
18045 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
18047 /* Build arrays of all existing indexes and their IndexInfos */
18049 foreach(cell
, attachRelIdxs
)
18051 Oid cldIdxId
= lfirst_oid(cell
);
18053 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
18054 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
18059 * If we're attaching a foreign table, we must fail if any of the indexes
18060 * is a constraint index; otherwise, there's nothing to do here. Do this
18061 * before starting work, to avoid wasting the effort of building a few
18062 * non-unique indexes before coming across a unique one.
18064 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
18066 foreach(cell
, idxes
)
18068 Oid idx
= lfirst_oid(cell
);
18069 Relation idxRel
= index_open(idx
, AccessShareLock
);
18071 if (idxRel
->rd_index
->indisunique
||
18072 idxRel
->rd_index
->indisprimary
)
18074 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18075 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18076 RelationGetRelationName(attachrel
),
18077 RelationGetRelationName(rel
)),
18078 errdetail("Partitioned table \"%s\" contains unique indexes.",
18079 RelationGetRelationName(rel
))));
18080 index_close(idxRel
, AccessShareLock
);
18087 * For each index on the partitioned table, find a matching one in the
18088 * partition-to-be; if one is not found, create one.
18090 foreach(cell
, idxes
)
18092 Oid idx
= lfirst_oid(cell
);
18093 Relation idxRel
= index_open(idx
, AccessShareLock
);
18096 bool found
= false;
18100 * Ignore indexes in the partitioned table other than partitioned
18103 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
18105 index_close(idxRel
, AccessShareLock
);
18109 /* construct an indexinfo to compare existing indexes against */
18110 info
= BuildIndexInfo(idxRel
);
18111 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
18112 RelationGetDescr(rel
),
18114 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
18117 * Scan the list of existing indexes in the partition-to-be, and mark
18118 * the first matching, valid, unattached one we find, if any, as
18119 * partition of the parent index. If we find one, we're done.
18121 for (i
= 0; i
< list_length(attachRelIdxs
); i
++)
18123 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
18124 Oid cldConstrOid
= InvalidOid
;
18126 /* does this index have a parent? if so, can't use it */
18127 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
18130 /* If this index is invalid, can't use it */
18131 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
18134 if (CompareIndexInfo(attachInfos
[i
], info
,
18135 attachrelIdxRels
[i
]->rd_indcollation
,
18136 idxRel
->rd_indcollation
,
18137 attachrelIdxRels
[i
]->rd_opfamily
,
18138 idxRel
->rd_opfamily
,
18142 * If this index is being created in the parent because of a
18143 * constraint, then the child needs to have a constraint also,
18144 * so look for one. If there is no such constraint, this
18145 * index is no good, so keep looking.
18147 if (OidIsValid(constraintOid
))
18150 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
18153 if (!OidIsValid(cldConstrOid
))
18158 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
18159 if (OidIsValid(constraintOid
))
18160 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
18161 RelationGetRelid(attachrel
));
18164 CommandCounterIncrement();
18170 * If no suitable index was found in the partition-to-be, create one
18178 stmt
= generateClonedIndexStmt(NULL
,
18181 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
18182 RelationGetRelid(idxRel
),
18185 true, false, false, false, false);
18188 index_close(idxRel
, AccessShareLock
);
18193 for (i
= 0; i
< list_length(attachRelIdxs
); i
++)
18194 index_close(attachrelIdxRels
[i
], AccessShareLock
);
18195 MemoryContextSwitchTo(oldcxt
);
18196 MemoryContextDelete(cxt
);
18200 * CloneRowTriggersToPartition
18201 * subroutine for ATExecAttachPartition/DefineRelation to create row
18202 * triggers on partitions
18205 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
18207 Relation pg_trigger
;
18211 MemoryContext perTupCxt
;
18213 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
18214 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
18215 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
18216 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
18217 true, NULL
, 1, &key
);
18219 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
18220 "clone trig", ALLOCSET_SMALL_SIZES
);
18222 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
18224 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
18225 CreateTrigStmt
*trigStmt
;
18230 List
*trigargs
= NIL
;
18231 MemoryContext oldcxt
;
18234 * Ignore statement-level triggers; those are not cloned.
18236 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
18240 * Don't clone internal triggers, because the constraint cloning code
18243 if (trigForm
->tgisinternal
)
18247 * Complain if we find an unexpected trigger type.
18249 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
18250 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
18251 elog(ERROR
, "unexpected trigger \"%s\" found",
18252 NameStr(trigForm
->tgname
));
18254 /* Use short-lived context for CREATE TRIGGER */
18255 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
18258 * If there is a WHEN clause, generate a 'cooked' version of it that's
18259 * appropriate for the partition.
18261 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
18262 RelationGetDescr(pg_trigger
), &isnull
);
18265 qual
= stringToNode(TextDatumGetCString(value
));
18266 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
18267 partition
, parent
);
18268 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
18269 partition
, parent
);
18273 * If there is a column list, transform it to a list of column names.
18274 * Note we don't need to map this list in any way ...
18276 if (trigForm
->tgattr
.dim1
> 0)
18280 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
18282 Form_pg_attribute col
;
18284 col
= TupleDescAttr(parent
->rd_att
,
18285 trigForm
->tgattr
.values
[i
] - 1);
18286 cols
= lappend(cols
,
18287 makeString(pstrdup(NameStr(col
->attname
))));
18291 /* Reconstruct trigger arguments list. */
18292 if (trigForm
->tgnargs
> 0)
18296 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
18297 RelationGetDescr(pg_trigger
), &isnull
);
18299 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18300 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
18302 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
18304 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
18306 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
18307 p
+= strlen(p
) + 1;
18311 trigStmt
= makeNode(CreateTrigStmt
);
18312 trigStmt
->replace
= false;
18313 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
18314 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
18315 trigStmt
->relation
= NULL
;
18316 trigStmt
->funcname
= NULL
; /* passed separately */
18317 trigStmt
->args
= trigargs
;
18318 trigStmt
->row
= true;
18319 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
18320 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
18321 trigStmt
->columns
= cols
;
18322 trigStmt
->whenClause
= NULL
; /* passed separately */
18323 trigStmt
->transitionRels
= NIL
; /* not supported at present */
18324 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
18325 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
18326 trigStmt
->constrrel
= NULL
; /* passed separately */
18328 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
18329 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
18330 trigForm
->tgfoid
, trigForm
->oid
, qual
,
18331 false, true, trigForm
->tgenabled
);
18333 MemoryContextSwitchTo(oldcxt
);
18334 MemoryContextReset(perTupCxt
);
18337 MemoryContextDelete(perTupCxt
);
18339 systable_endscan(scan
);
18340 table_close(pg_trigger
, RowExclusiveLock
);
18344 * ALTER TABLE DETACH PARTITION
18346 * Return the address of the relation that is no longer a partition of rel.
18348 * If concurrent mode is requested, we run in two transactions. A side-
18349 * effect is that this command cannot run in a multi-part ALTER TABLE.
18350 * Currently, that's enforced by the grammar.
18352 * The strategy for concurrency is to first modify the partition's
18353 * pg_inherit catalog row to make it visible to everyone that the
18354 * partition is detached, lock the partition against writes, and commit
18355 * the transaction; anyone who requests the partition descriptor from
18356 * that point onwards has to ignore such a partition. In a second
18357 * transaction, we wait until all transactions that could have seen the
18358 * partition as attached are gone, then we remove the rest of partition
18359 * metadata (pg_inherits and pg_class.relpartbounds).
18361 static ObjectAddress
18362 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
18363 RangeVar
*name
, bool concurrent
)
18366 ObjectAddress address
;
18367 Oid defaultPartOid
;
18370 * We must lock the default partition, because detaching this partition
18371 * will change its partition constraint.
18374 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
18375 if (OidIsValid(defaultPartOid
))
18378 * Concurrent detaching when a default partition exists is not
18379 * supported. The main problem is that the default partition
18380 * constraint would change. And there's a definitional problem: what
18381 * should happen to the tuples that are being inserted that belong to
18382 * the partition being detached? Putting them on the partition being
18383 * detached would be wrong, since they'd become "lost" after the
18384 * detaching completes but we cannot put them in the default partition
18385 * either until we alter its partition constraint.
18387 * I think we could solve this problem if we effected the constraint
18388 * change before committing the first transaction. But the lock would
18389 * have to remain AEL and it would cause concurrent query planning to
18390 * be blocked, so changing it that way would be even worse.
18394 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
18395 errmsg("cannot detach partitions concurrently when a default partition exists")));
18396 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
18400 * In concurrent mode, the partition is locked with share-update-exclusive
18401 * in the first transaction. This allows concurrent transactions to be
18402 * doing DML to the partition.
18404 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
18405 AccessExclusiveLock
);
18408 * Check inheritance conditions and either delete the pg_inherits row (in
18409 * non-concurrent mode) or just set the inhdetachpending flag.
18412 RemoveInheritance(partRel
, rel
, false);
18414 MarkInheritDetached(partRel
, rel
);
18417 * Ensure that foreign keys still hold after this detach. This keeps
18418 * locks on the referencing tables, which prevents concurrent transactions
18419 * from adding rows that we wouldn't see. For this to work in concurrent
18420 * mode, it is critical that the partition appears as no longer attached
18421 * for the RI queries as soon as the first transaction commits.
18423 ATDetachCheckNoForeignKeyRefs(partRel
);
18426 * Concurrent mode has to work harder; first we add a new constraint to
18427 * the partition that matches the partition constraint. Then we close our
18428 * existing transaction, and in a new one wait for all processes to catch
18429 * up on the catalog updates we've done so far; at that point we can
18430 * complete the operation.
18437 char *parentrelname
;
18441 * Add a new constraint to the partition being detached, which
18442 * supplants the partition constraint (unless there is one already).
18444 DetachAddConstraintIfNeeded(wqueue
, partRel
);
18447 * We're almost done now; the only traces that remain are the
18448 * pg_inherits tuple and the partition's relpartbounds. Before we can
18449 * remove those, we need to wait until all transactions that know that
18450 * this is a partition are gone.
18454 * Remember relation OIDs to re-acquire them later; and relation names
18455 * too, for error messages if something is dropped in between.
18457 partrelid
= RelationGetRelid(partRel
);
18458 parentrelid
= RelationGetRelid(rel
);
18459 parentrelname
= MemoryContextStrdup(PortalContext
,
18460 RelationGetRelationName(rel
));
18461 partrelname
= MemoryContextStrdup(PortalContext
,
18462 RelationGetRelationName(partRel
));
18464 /* Invalidate relcache entries for the parent -- must be before close */
18465 CacheInvalidateRelcache(rel
);
18467 table_close(partRel
, NoLock
);
18468 table_close(rel
, NoLock
);
18471 /* Make updated catalog entry visible */
18472 PopActiveSnapshot();
18473 CommitTransactionCommand();
18475 StartTransactionCommand();
18478 * Now wait. This ensures that all queries that were planned
18479 * including the partition are finished before we remove the rest of
18480 * catalog entries. We don't need or indeed want to acquire this
18481 * lock, though -- that would block later queries.
18483 * We don't need to concern ourselves with waiting for a lock on the
18484 * partition itself, since we will acquire AccessExclusiveLock below.
18486 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
18487 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
18490 * Now acquire locks in both relations again. Note they may have been
18491 * removed in the meantime, so care is required.
18493 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
18494 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
18496 /* If the relations aren't there, something bad happened; bail out */
18499 if (partRel
!= NULL
) /* shouldn't happen */
18500 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
18503 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
18504 errmsg("partitioned table \"%s\" was removed concurrently",
18507 if (partRel
== NULL
)
18509 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
18510 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
18515 /* Do the final part of detaching */
18516 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
18518 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
18520 /* keep our lock until commit */
18521 table_close(partRel
, NoLock
);
18527 * Second part of ALTER TABLE .. DETACH.
18529 * This is separate so that it can be run independently when the second
18530 * transaction of the concurrent algorithm fails (crash or abort).
18533 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
18534 Oid defaultPartOid
)
18540 Datum new_val
[Natts_pg_class
];
18541 bool new_null
[Natts_pg_class
],
18542 new_repl
[Natts_pg_class
];
18545 Relation trigrel
= NULL
;
18550 * We can remove the pg_inherits row now. (In the non-concurrent case,
18551 * this was already done).
18553 RemoveInheritance(partRel
, rel
, true);
18556 /* Drop any triggers that were cloned on creation/attach. */
18557 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
18560 * Detach any foreign keys that are inherited. This includes creating
18561 * additional action triggers.
18563 fks
= copyObject(RelationGetFKeyList(partRel
));
18565 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
18568 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
18570 Form_pg_constraint conform
;
18571 Constraint
*fkconstraint
;
18572 Oid insertTriggerOid
,
18575 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
18576 if (!HeapTupleIsValid(contup
))
18577 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
18578 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
18580 /* consider only the inherited foreign keys */
18581 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
18582 !OidIsValid(conform
->conparentid
))
18584 ReleaseSysCache(contup
);
18588 /* unset conparentid and adjust conislocal, coninhcount, etc. */
18589 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
18592 * Also, look up the partition's "check" triggers corresponding to the
18593 * constraint being detached and detach them from the parent triggers.
18595 GetForeignKeyCheckTriggers(trigrel
,
18596 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
18597 &insertTriggerOid
, &updateTriggerOid
);
18598 Assert(OidIsValid(insertTriggerOid
));
18599 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
18600 RelationGetRelid(partRel
));
18601 Assert(OidIsValid(updateTriggerOid
));
18602 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
18603 RelationGetRelid(partRel
));
18606 * Make the action triggers on the referenced relation. When this was
18607 * a partition the action triggers pointed to the parent rel (they
18608 * still do), but now we need separate ones of our own.
18610 fkconstraint
= makeNode(Constraint
);
18611 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
18612 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
18613 fkconstraint
->deferrable
= conform
->condeferrable
;
18614 fkconstraint
->initdeferred
= conform
->condeferred
;
18615 fkconstraint
->location
= -1;
18616 fkconstraint
->pktable
= NULL
;
18617 fkconstraint
->fk_attrs
= NIL
;
18618 fkconstraint
->pk_attrs
= NIL
;
18619 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
18620 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
18621 fkconstraint
->fk_del_action
= conform
->confdeltype
;
18622 fkconstraint
->fk_del_set_cols
= NIL
;
18623 fkconstraint
->old_conpfeqop
= NIL
;
18624 fkconstraint
->old_pktable_oid
= InvalidOid
;
18625 fkconstraint
->skip_validation
= false;
18626 fkconstraint
->initially_valid
= true;
18628 createForeignKeyActionTriggers(partRel
, conform
->confrelid
,
18629 fkconstraint
, fk
->conoid
,
18631 InvalidOid
, InvalidOid
,
18634 ReleaseSysCache(contup
);
18636 list_free_deep(fks
);
18638 table_close(trigrel
, RowExclusiveLock
);
18641 * Any sub-constraints that are in the referenced-side of a larger
18642 * constraint have to be removed. This partition is no longer part of the
18643 * key space of the constraint.
18645 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
18647 Oid constrOid
= lfirst_oid(cell
);
18648 ObjectAddress constraint
;
18650 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
18651 deleteDependencyRecordsForClass(ConstraintRelationId
,
18653 ConstraintRelationId
,
18654 DEPENDENCY_INTERNAL
);
18655 CommandCounterIncrement();
18657 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
18658 performDeletion(&constraint
, DROP_RESTRICT
, 0);
18661 /* Now we can detach indexes */
18662 indexes
= RelationGetIndexList(partRel
);
18663 foreach(cell
, indexes
)
18665 Oid idxid
= lfirst_oid(cell
);
18669 if (!has_superclass(idxid
))
18672 Assert((IndexGetRelation(get_partition_parent(idxid
, false), false) ==
18673 RelationGetRelid(rel
)));
18675 idx
= index_open(idxid
, AccessExclusiveLock
);
18676 IndexSetParentIndex(idx
, InvalidOid
);
18678 /* If there's a constraint associated with the index, detach it too */
18679 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
18681 if (OidIsValid(constrOid
))
18682 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
18684 index_close(idx
, NoLock
);
18687 /* Update pg_class tuple */
18688 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
18689 tuple
= SearchSysCacheCopy1(RELOID
,
18690 ObjectIdGetDatum(RelationGetRelid(partRel
)));
18691 if (!HeapTupleIsValid(tuple
))
18692 elog(ERROR
, "cache lookup failed for relation %u",
18693 RelationGetRelid(partRel
));
18694 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
18696 /* Clear relpartbound and reset relispartition */
18697 memset(new_val
, 0, sizeof(new_val
));
18698 memset(new_null
, false, sizeof(new_null
));
18699 memset(new_repl
, false, sizeof(new_repl
));
18700 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
18701 new_null
[Anum_pg_class_relpartbound
- 1] = true;
18702 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
18703 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
18704 new_val
, new_null
, new_repl
);
18706 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
18707 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
18708 heap_freetuple(newtuple
);
18709 table_close(classRel
, RowExclusiveLock
);
18711 if (OidIsValid(defaultPartOid
))
18714 * If the relation being detached is the default partition itself,
18715 * remove it from the parent's pg_partitioned_table entry.
18717 * If not, we must invalidate default partition's relcache entry, as
18718 * in StorePartitionBound: its partition constraint depends on every
18719 * other partition's partition constraint.
18721 if (RelationGetRelid(partRel
) == defaultPartOid
)
18722 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
18724 CacheInvalidateRelcacheByRelid(defaultPartOid
);
18728 * Invalidate the parent's relcache so that the partition is no longer
18729 * included in its partition descriptor.
18731 CacheInvalidateRelcache(rel
);
18734 * If the partition we just detached is partitioned itself, invalidate
18735 * relcache for all descendent partitions too to ensure that their
18736 * rd_partcheck expression trees are rebuilt; must lock partitions before
18737 * doing so, using the same lockmode as what partRel has been locked with
18740 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18744 children
= find_all_inheritors(RelationGetRelid(partRel
),
18745 AccessExclusiveLock
, NULL
);
18746 foreach(cell
, children
)
18748 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
18754 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
18756 * To use when a DETACH PARTITION command previously did not run to
18757 * completion; this completes the detaching process.
18759 static ObjectAddress
18760 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
18763 ObjectAddress address
;
18764 Snapshot snap
= GetActiveSnapshot();
18766 partRel
= table_openrv(name
, AccessExclusiveLock
);
18769 * Wait until existing snapshots are gone. This is important if the
18770 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
18771 * user could immediately run DETACH FINALIZE without actually waiting for
18772 * existing transactions. We must not complete the detach action until
18773 * all such queries are complete (otherwise we would present them with an
18774 * inconsistent view of catalogs).
18776 WaitForOlderSnapshots(snap
->xmin
, false);
18778 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
18780 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
18782 table_close(partRel
, NoLock
);
18788 * DetachAddConstraintIfNeeded
18789 * Subroutine for ATExecDetachPartition. Create a constraint that
18790 * takes the place of the partition constraint, but avoid creating
18791 * a dupe if an constraint already exists which implies the needed
18795 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
18797 List
*constraintExpr
;
18799 constraintExpr
= RelationGetPartitionQual(partRel
);
18800 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
18803 * Avoid adding a new constraint if the needed constraint is implied by an
18804 * existing constraint
18806 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
18808 AlteredTableInfo
*tab
;
18811 tab
= ATGetQueueEntry(wqueue
, partRel
);
18813 /* Add constraint on partition, equivalent to the partition constraint */
18814 n
= makeNode(Constraint
);
18815 n
->contype
= CONSTR_CHECK
;
18818 n
->is_no_inherit
= false;
18819 n
->raw_expr
= NULL
;
18820 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
18821 n
->initially_valid
= true;
18822 n
->skip_validation
= true;
18823 /* It's a re-add, since it nominally already exists */
18824 ATAddCheckConstraint(wqueue
, tab
, partRel
, n
,
18825 true, false, true, ShareUpdateExclusiveLock
);
18830 * DropClonedTriggersFromPartition
18831 * subroutine for ATExecDetachPartition to remove any triggers that were
18832 * cloned to the partition when it was created-as-partition or attached.
18833 * This undoes what CloneRowTriggersToPartition did.
18836 DropClonedTriggersFromPartition(Oid partitionId
)
18842 ObjectAddresses
*objects
;
18844 objects
= new_object_addresses();
18847 * Scan pg_trigger to search for all triggers on this rel.
18849 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
18850 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
18851 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
18852 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
18853 true, NULL
, 1, &skey
);
18854 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
18856 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
18857 ObjectAddress trig
;
18859 /* Ignore triggers that weren't cloned */
18860 if (!OidIsValid(pg_trigger
->tgparentid
))
18864 * Ignore internal triggers that are implementation objects of foreign
18865 * keys, because these will be detached when the foreign keys
18868 if (OidIsValid(pg_trigger
->tgconstrrelid
))
18872 * This is ugly, but necessary: remove the dependency markings on the
18873 * trigger so that it can be removed.
18875 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
18877 DEPENDENCY_PARTITION_PRI
);
18878 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
18879 RelationRelationId
,
18880 DEPENDENCY_PARTITION_SEC
);
18882 /* remember this trigger to remove it below */
18883 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
18884 add_exact_object_address(&trig
, objects
);
18887 /* make the dependency removal visible to the deletion below */
18888 CommandCounterIncrement();
18889 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
18892 free_object_addresses(objects
);
18893 systable_endscan(scan
);
18894 table_close(tgrel
, RowExclusiveLock
);
18898 * Before acquiring lock on an index, acquire the same lock on the owning
18901 struct AttachIndexCallbackState
18905 bool lockedParentTbl
;
18909 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
18912 struct AttachIndexCallbackState
*state
;
18913 Form_pg_class classform
;
18916 state
= (struct AttachIndexCallbackState
*) arg
;
18918 if (!state
->lockedParentTbl
)
18920 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
18921 state
->lockedParentTbl
= true;
18925 * If we previously locked some other heap, and the name we're looking up
18926 * no longer refers to an index on that relation, release the now-useless
18927 * lock. XXX maybe we should do *after* we verify whether the index does
18928 * not actually belong to the same relation ...
18930 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
18932 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
18933 state
->partitionOid
= InvalidOid
;
18936 /* Didn't find a relation, so no need for locking or permission checks. */
18937 if (!OidIsValid(relOid
))
18940 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
18941 if (!HeapTupleIsValid(tuple
))
18942 return; /* concurrently dropped, so nothing to do */
18943 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
18944 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
18945 classform
->relkind
!= RELKIND_INDEX
)
18947 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18948 errmsg("\"%s\" is not an index", rv
->relname
)));
18949 ReleaseSysCache(tuple
);
18952 * Since we need only examine the heap's tupledesc, an access share lock
18953 * on it (preventing any DDL) is sufficient.
18955 state
->partitionOid
= IndexGetRelation(relOid
, false);
18956 LockRelationOid(state
->partitionOid
, AccessShareLock
);
18960 * ALTER INDEX i1 ATTACH PARTITION i2
18962 static ObjectAddress
18963 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
18967 Relation parentTbl
;
18968 ObjectAddress address
;
18971 struct AttachIndexCallbackState state
;
18974 * We need to obtain lock on the index 'name' to modify it, but we also
18975 * need to read its owning table's tuple descriptor -- so we need to lock
18976 * both. To avoid deadlocks, obtain lock on the table before doing so on
18977 * the index. Furthermore, we need to examine the parent table of the
18978 * partition, so lock that one too.
18980 state
.partitionOid
= InvalidOid
;
18981 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
18982 state
.lockedParentTbl
= false;
18984 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
18985 RangeVarCallbackForAttachIndex
,
18988 if (!OidIsValid(partIdxId
))
18990 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18991 errmsg("index \"%s\" does not exist", name
->relname
)));
18993 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
18994 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
18996 /* we already hold locks on both tables, so this is safe: */
18997 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
18998 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
19000 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
19002 /* Silently do nothing if already in the right state */
19003 currParent
= partIdx
->rd_rel
->relispartition
?
19004 get_partition_parent(partIdxId
, false) : InvalidOid
;
19005 if (currParent
!= RelationGetRelid(parentIdx
))
19007 IndexInfo
*childInfo
;
19008 IndexInfo
*parentInfo
;
19012 PartitionDesc partDesc
;
19014 cldConstrId
= InvalidOid
;
19017 * If this partition already has an index attached, refuse the
19020 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
19022 if (OidIsValid(currParent
))
19024 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19025 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19026 RelationGetRelationName(partIdx
),
19027 RelationGetRelationName(parentIdx
)),
19028 errdetail("Index \"%s\" is already attached to another index.",
19029 RelationGetRelationName(partIdx
))));
19031 /* Make sure it indexes a partition of the other index's table */
19032 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
19034 for (i
= 0; i
< partDesc
->nparts
; i
++)
19036 if (partDesc
->oids
[i
] == state
.partitionOid
)
19044 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19045 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19046 RelationGetRelationName(partIdx
),
19047 RelationGetRelationName(parentIdx
)),
19048 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19049 RelationGetRelationName(partIdx
),
19050 RelationGetRelationName(parentTbl
))));
19052 /* Ensure the indexes are compatible */
19053 childInfo
= BuildIndexInfo(partIdx
);
19054 parentInfo
= BuildIndexInfo(parentIdx
);
19055 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
19056 RelationGetDescr(parentTbl
),
19058 if (!CompareIndexInfo(childInfo
, parentInfo
,
19059 partIdx
->rd_indcollation
,
19060 parentIdx
->rd_indcollation
,
19061 partIdx
->rd_opfamily
,
19062 parentIdx
->rd_opfamily
,
19065 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19066 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19067 RelationGetRelationName(partIdx
),
19068 RelationGetRelationName(parentIdx
)),
19069 errdetail("The index definitions do not match.")));
19072 * If there is a constraint in the parent, make sure there is one in
19075 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
19076 RelationGetRelid(parentIdx
));
19078 if (OidIsValid(constraintOid
))
19080 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
19082 if (!OidIsValid(cldConstrId
))
19084 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19085 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19086 RelationGetRelationName(partIdx
),
19087 RelationGetRelationName(parentIdx
)),
19088 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19089 RelationGetRelationName(parentIdx
),
19090 RelationGetRelationName(parentTbl
),
19091 RelationGetRelationName(partIdx
))));
19094 /* All good -- do it */
19095 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
19096 if (OidIsValid(constraintOid
))
19097 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
19098 RelationGetRelid(partTbl
));
19100 free_attrmap(attmap
);
19102 validatePartitionedIndex(parentIdx
, parentTbl
);
19105 relation_close(parentTbl
, AccessShareLock
);
19106 /* keep these locks till commit */
19107 relation_close(partTbl
, NoLock
);
19108 relation_close(partIdx
, NoLock
);
19114 * Verify whether the given partition already contains an index attached
19115 * to the given partitioned index. If so, raise an error.
19118 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
19122 existingIdx
= index_get_partition(partitionTbl
,
19123 RelationGetRelid(parentIdx
));
19124 if (OidIsValid(existingIdx
))
19126 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19127 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19128 RelationGetRelationName(partIdx
),
19129 RelationGetRelationName(parentIdx
)),
19130 errdetail("Another index is already attached for partition \"%s\".",
19131 RelationGetRelationName(partitionTbl
))));
19135 * Verify whether the set of attached partition indexes to a parent index on
19136 * a partitioned table is complete. If it is, mark the parent index valid.
19138 * This should be called each time a partition index is attached.
19141 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
19143 Relation inheritsRel
;
19148 bool updated
= false;
19150 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
19153 * Scan pg_inherits for this parent index. Count each valid index we find
19154 * (verifying the pg_index entry for each), and if we reach the total
19155 * amount we expect, we can mark this parent index as valid.
19157 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
19158 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
19159 BTEqualStrategyNumber
, F_OIDEQ
,
19160 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19161 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
19163 while ((inhTup
= systable_getnext(scan
)) != NULL
)
19165 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
19167 Form_pg_index indexForm
;
19169 indTup
= SearchSysCache1(INDEXRELID
,
19170 ObjectIdGetDatum(inhForm
->inhrelid
));
19171 if (!HeapTupleIsValid(indTup
))
19172 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
19173 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19174 if (indexForm
->indisvalid
)
19176 ReleaseSysCache(indTup
);
19179 /* Done with pg_inherits */
19180 systable_endscan(scan
);
19181 table_close(inheritsRel
, AccessShareLock
);
19184 * If we found as many inherited indexes as the partitioned table has
19185 * partitions, we're good; update pg_index to set indisvalid.
19187 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
19191 Form_pg_index indexForm
;
19193 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
19194 indTup
= SearchSysCacheCopy1(INDEXRELID
,
19195 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19196 if (!HeapTupleIsValid(indTup
))
19197 elog(ERROR
, "cache lookup failed for index %u",
19198 RelationGetRelid(partedIdx
));
19199 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19201 indexForm
->indisvalid
= true;
19204 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
19206 table_close(idxRel
, RowExclusiveLock
);
19207 heap_freetuple(indTup
);
19211 * If this index is in turn a partition of a larger index, validating it
19212 * might cause the parent to become valid also. Try that.
19214 if (updated
&& partedIdx
->rd_rel
->relispartition
)
19218 Relation parentIdx
,
19221 /* make sure we see the validation we just did */
19222 CommandCounterIncrement();
19224 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
19225 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
19226 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
19227 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
19228 Assert(!parentIdx
->rd_index
->indisvalid
);
19230 validatePartitionedIndex(parentIdx
, parentTbl
);
19232 relation_close(parentIdx
, AccessExclusiveLock
);
19233 relation_close(parentTbl
, AccessExclusiveLock
);
19238 * Return an OID list of constraints that reference the given relation
19239 * that are marked as having a parent constraints.
19242 GetParentedForeignKeyRefs(Relation partition
)
19244 Relation pg_constraint
;
19247 ScanKeyData key
[2];
19248 List
*constraints
= NIL
;
19251 * If no indexes, or no columns are referenceable by FKs, we can avoid the
19254 if (RelationGetIndexList(partition
) == NIL
||
19255 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
19256 INDEX_ATTR_BITMAP_KEY
)))
19259 /* Search for constraints referencing this table */
19260 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
19261 ScanKeyInit(&key
[0],
19262 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
19263 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
19264 ScanKeyInit(&key
[1],
19265 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
19266 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
19268 /* XXX This is a seqscan, as we don't have a usable index */
19269 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
19270 while ((tuple
= systable_getnext(scan
)) != NULL
)
19272 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
19275 * We only need to process constraints that are part of larger ones.
19277 if (!OidIsValid(constrForm
->conparentid
))
19280 constraints
= lappend_oid(constraints
, constrForm
->oid
);
19283 systable_endscan(scan
);
19284 table_close(pg_constraint
, AccessShareLock
);
19286 return constraints
;
19290 * During DETACH PARTITION, verify that any foreign keys pointing to the
19291 * partitioned table would not become invalid. An error is raised if any
19292 * referenced values exist.
19295 ATDetachCheckNoForeignKeyRefs(Relation partition
)
19300 constraints
= GetParentedForeignKeyRefs(partition
);
19302 foreach(cell
, constraints
)
19304 Oid constrOid
= lfirst_oid(cell
);
19306 Form_pg_constraint constrForm
;
19308 Trigger trig
= {0};
19310 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
19311 if (!HeapTupleIsValid(tuple
))
19312 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
19313 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
19315 Assert(OidIsValid(constrForm
->conparentid
));
19316 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
19318 /* prevent data changes into the referencing table until commit */
19319 rel
= table_open(constrForm
->conrelid
, ShareLock
);
19321 trig
.tgoid
= InvalidOid
;
19322 trig
.tgname
= NameStr(constrForm
->conname
);
19323 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
19324 trig
.tgisinternal
= true;
19325 trig
.tgconstrrelid
= RelationGetRelid(partition
);
19326 trig
.tgconstrindid
= constrForm
->conindid
;
19327 trig
.tgconstraint
= constrForm
->oid
;
19328 trig
.tgdeferrable
= false;
19329 trig
.tginitdeferred
= false;
19330 /* we needn't fill in remaining fields */
19332 RI_PartitionRemove_Check(&trig
, rel
, partition
);
19334 ReleaseSysCache(tuple
);
19336 table_close(rel
, NoLock
);
19341 * resolve column compression specification to compression method.
19344 GetAttributeCompression(Oid atttypid
, char *compression
)
19348 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
19349 return InvalidCompressionMethod
;
19352 * To specify a nondefault method, the column data type must be toastable.
19353 * Note this says nothing about whether the column's attstorage setting
19354 * permits compression; we intentionally allow attstorage and
19355 * attcompression to be independent. But with a non-toastable type,
19356 * attstorage could not be set to a value that would permit compression.
19358 * We don't actually need to enforce this, since nothing bad would happen
19359 * if attcompression were non-default; it would never be consulted. But
19360 * it seems more user-friendly to complain about a certainly-useless
19361 * attempt to set the property.
19363 if (!TypeIsToastable(atttypid
))
19365 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
19366 errmsg("column data type %s does not support compression",
19367 format_type_be(atttypid
))));
19369 cmethod
= CompressionNameToMethod(compression
);
19370 if (!CompressionMethodIsValid(cmethod
))
19372 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
19373 errmsg("invalid compression method \"%s\"", compression
)));
19379 * resolve column storage specification
19382 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
19386 if (pg_strcasecmp(storagemode
, "plain") == 0)
19387 cstorage
= TYPSTORAGE_PLAIN
;
19388 else if (pg_strcasecmp(storagemode
, "external") == 0)
19389 cstorage
= TYPSTORAGE_EXTERNAL
;
19390 else if (pg_strcasecmp(storagemode
, "extended") == 0)
19391 cstorage
= TYPSTORAGE_EXTENDED
;
19392 else if (pg_strcasecmp(storagemode
, "main") == 0)
19393 cstorage
= TYPSTORAGE_MAIN
;
19394 else if (pg_strcasecmp(storagemode
, "default") == 0)
19395 cstorage
= get_typstorage(atttypid
);
19398 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
19399 errmsg("invalid storage type \"%s\"",
19403 * safety check: do not allow toasted storage modes unless column datatype
19406 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
19408 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
19409 errmsg("column data type %s can only have storage PLAIN",
19410 format_type_be(atttypid
))));