1 /*-------------------------------------------------------------------------
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/gist.h"
20 #include "access/heapam.h"
21 #include "access/heapam_xlog.h"
22 #include "access/multixact.h"
23 #include "access/reloptions.h"
24 #include "access/relscan.h"
25 #include "access/sysattr.h"
26 #include "access/tableam.h"
27 #include "access/toast_compression.h"
28 #include "access/xact.h"
29 #include "access/xlog.h"
30 #include "access/xloginsert.h"
31 #include "catalog/catalog.h"
32 #include "catalog/heap.h"
33 #include "catalog/index.h"
34 #include "catalog/namespace.h"
35 #include "catalog/objectaccess.h"
36 #include "catalog/partition.h"
37 #include "catalog/pg_am.h"
38 #include "catalog/pg_attrdef.h"
39 #include "catalog/pg_collation.h"
40 #include "catalog/pg_constraint.h"
41 #include "catalog/pg_depend.h"
42 #include "catalog/pg_foreign_table.h"
43 #include "catalog/pg_inherits.h"
44 #include "catalog/pg_largeobject.h"
45 #include "catalog/pg_namespace.h"
46 #include "catalog/pg_opclass.h"
47 #include "catalog/pg_statistic_ext.h"
48 #include "catalog/pg_tablespace.h"
49 #include "catalog/pg_trigger.h"
50 #include "catalog/pg_type.h"
51 #include "catalog/storage.h"
52 #include "catalog/storage_xlog.h"
53 #include "catalog/toasting.h"
54 #include "commands/cluster.h"
55 #include "commands/comment.h"
56 #include "commands/defrem.h"
57 #include "commands/event_trigger.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_coerce.h"
74 #include "parser/parse_collate.h"
75 #include "parser/parse_expr.h"
76 #include "parser/parse_relation.h"
77 #include "parser/parse_type.h"
78 #include "parser/parse_utilcmd.h"
79 #include "parser/parser.h"
80 #include "partitioning/partbounds.h"
81 #include "partitioning/partdesc.h"
83 #include "rewrite/rewriteDefine.h"
84 #include "rewrite/rewriteHandler.h"
85 #include "rewrite/rewriteManip.h"
86 #include "storage/bufmgr.h"
87 #include "storage/lmgr.h"
88 #include "storage/lock.h"
89 #include "storage/predicate.h"
90 #include "storage/smgr.h"
91 #include "tcop/utility.h"
92 #include "utils/acl.h"
93 #include "utils/builtins.h"
94 #include "utils/fmgroids.h"
95 #include "utils/inval.h"
96 #include "utils/lsyscache.h"
97 #include "utils/memutils.h"
98 #include "utils/partcache.h"
99 #include "utils/relcache.h"
100 #include "utils/ruleutils.h"
101 #include "utils/snapmgr.h"
102 #include "utils/syscache.h"
103 #include "utils/timestamp.h"
104 #include "utils/typcache.h"
105 #include "utils/usercontext.h"
108 * ON COMMIT action list
110 typedef struct OnCommitItem
112 Oid relid
; /* relid of relation */
113 OnCommitAction oncommit
; /* what to do at end of xact */
116 * If this entry was created during the current transaction,
117 * creating_subid is the ID of the creating subxact; if created in a prior
118 * transaction, creating_subid is zero. If deleted during the current
119 * transaction, deleting_subid is the ID of the deleting subxact; if no
120 * deletion request is pending, deleting_subid is zero.
122 SubTransactionId creating_subid
;
123 SubTransactionId deleting_subid
;
126 static List
*on_commits
= NIL
;
130 * State information for ALTER TABLE
132 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
133 * structs, one for each table modified by the operation (the named table
134 * plus any child tables that are affected). We save lists of subcommands
135 * to apply to this table (possibly modified by parse transformation steps);
136 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
137 * necessary information is stored in the constraints and newvals lists.
139 * Phase 2 is divided into multiple passes; subcommands are executed in
140 * a pass determined by subcommand type.
143 typedef enum AlterTablePass
145 AT_PASS_UNSET
= -1, /* UNSET will cause ERROR */
146 AT_PASS_DROP
, /* DROP (all flavors) */
147 AT_PASS_ALTER_TYPE
, /* ALTER COLUMN TYPE */
148 AT_PASS_ADD_COL
, /* ADD COLUMN */
149 AT_PASS_SET_EXPRESSION
, /* ALTER SET EXPRESSION */
150 AT_PASS_OLD_INDEX
, /* re-add existing indexes */
151 AT_PASS_OLD_CONSTR
, /* re-add existing constraints */
152 /* We could support a RENAME COLUMN pass here, but not currently used */
153 AT_PASS_ADD_CONSTR
, /* ADD constraints (initial examination) */
154 AT_PASS_COL_ATTRS
, /* set column attributes, eg NOT NULL */
155 AT_PASS_ADD_INDEXCONSTR
, /* ADD index-based constraints */
156 AT_PASS_ADD_INDEX
, /* ADD indexes */
157 AT_PASS_ADD_OTHERCONSTR
, /* ADD other constraints, defaults */
158 AT_PASS_MISC
, /* other stuff */
161 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
163 typedef struct AlteredTableInfo
165 /* Information saved before any work commences: */
166 Oid relid
; /* Relation to work on */
167 char relkind
; /* Its relkind */
168 TupleDesc oldDesc
; /* Pre-modification tuple descriptor */
171 * Transiently set during Phase 2, normally set to NULL.
173 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
174 * returns control. This can be exploited by ATExecCmd subroutines to
175 * close/reopen across transaction boundaries.
179 /* Information saved by Phase 1 for Phase 2: */
180 List
*subcmds
[AT_NUM_PASSES
]; /* Lists of AlterTableCmd */
181 /* Information saved by Phases 1/2 for Phase 3: */
182 List
*constraints
; /* List of NewConstraint */
183 List
*newvals
; /* List of NewColumnValue */
184 List
*afterStmts
; /* List of utility command parsetrees */
185 bool verify_new_notnull
; /* T if we should recheck NOT NULL */
186 int rewrite
; /* Reason for forced rewrite, if any */
187 bool chgAccessMethod
; /* T if SET ACCESS METHOD is used */
188 Oid newAccessMethod
; /* new access method; 0 means no change,
189 * if above is true */
190 Oid newTableSpace
; /* new tablespace; 0 means no change */
191 bool chgPersistence
; /* T if SET LOGGED/UNLOGGED is used */
192 char newrelpersistence
; /* if above is true */
193 Expr
*partition_constraint
; /* for attach partition validation */
194 /* true, if validating default due to some other attach/detach */
195 bool validate_default
;
196 /* Objects to rebuild after completing ALTER TYPE operations */
197 List
*changedConstraintOids
; /* OIDs of constraints to rebuild */
198 List
*changedConstraintDefs
; /* string definitions of same */
199 List
*changedIndexOids
; /* OIDs of indexes to rebuild */
200 List
*changedIndexDefs
; /* string definitions of same */
201 char *replicaIdentityIndex
; /* index to reset as REPLICA IDENTITY */
202 char *clusterOnIndex
; /* index to use for CLUSTER */
203 List
*changedStatisticsOids
; /* OIDs of statistics to rebuild */
204 List
*changedStatisticsDefs
; /* string definitions of same */
207 /* Struct describing one new constraint to check in Phase 3 scan */
208 /* Note: new not-null constraints are handled elsewhere */
209 typedef struct NewConstraint
211 char *name
; /* Constraint name, or NULL if none */
212 ConstrType contype
; /* CHECK or FOREIGN */
213 Oid refrelid
; /* PK rel, if FOREIGN */
214 Oid refindid
; /* OID of PK's index, if FOREIGN */
215 bool conwithperiod
; /* Whether the new FOREIGN KEY uses PERIOD */
216 Oid conid
; /* OID of pg_constraint entry, if FOREIGN */
217 Node
*qual
; /* Check expr or CONSTR_FOREIGN Constraint */
218 ExprState
*qualstate
; /* Execution state for CHECK expr */
222 * Struct describing one new column value that needs to be computed during
223 * Phase 3 copy (this could be either a new column with a non-null default, or
224 * a column that we're changing the type of). Columns without such an entry
225 * are just copied from the old table during ATRewriteTable. Note that the
226 * expr is an expression over *old* table values, except when is_generated
227 * is true; then it is an expression over columns of the *new* tuple.
229 typedef struct NewColumnValue
231 AttrNumber attnum
; /* which column */
232 Expr
*expr
; /* expression to compute */
233 ExprState
*exprstate
; /* execution state */
234 bool is_generated
; /* is it a GENERATED expression? */
238 * Error-reporting support for RemoveRelations
240 struct dropmsgstrings
243 int nonexistent_code
;
244 const char *nonexistent_msg
;
245 const char *skipping_msg
;
246 const char *nota_msg
;
247 const char *drophint_msg
;
250 static const struct dropmsgstrings dropmsgstringarray
[] = {
252 ERRCODE_UNDEFINED_TABLE
,
253 gettext_noop("table \"%s\" does not exist"),
254 gettext_noop("table \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a table"),
256 gettext_noop("Use DROP TABLE to remove a table.")},
258 ERRCODE_UNDEFINED_TABLE
,
259 gettext_noop("sequence \"%s\" does not exist"),
260 gettext_noop("sequence \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a sequence"),
262 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
264 ERRCODE_UNDEFINED_TABLE
,
265 gettext_noop("view \"%s\" does not exist"),
266 gettext_noop("view \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a view"),
268 gettext_noop("Use DROP VIEW to remove a view.")},
270 ERRCODE_UNDEFINED_TABLE
,
271 gettext_noop("materialized view \"%s\" does not exist"),
272 gettext_noop("materialized view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a materialized view"),
274 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
276 ERRCODE_UNDEFINED_OBJECT
,
277 gettext_noop("index \"%s\" does not exist"),
278 gettext_noop("index \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not an index"),
280 gettext_noop("Use DROP INDEX to remove an index.")},
281 {RELKIND_COMPOSITE_TYPE
,
282 ERRCODE_UNDEFINED_OBJECT
,
283 gettext_noop("type \"%s\" does not exist"),
284 gettext_noop("type \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a type"),
286 gettext_noop("Use DROP TYPE to remove a type.")},
287 {RELKIND_FOREIGN_TABLE
,
288 ERRCODE_UNDEFINED_OBJECT
,
289 gettext_noop("foreign table \"%s\" does not exist"),
290 gettext_noop("foreign table \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a foreign table"),
292 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
293 {RELKIND_PARTITIONED_TABLE
,
294 ERRCODE_UNDEFINED_TABLE
,
295 gettext_noop("table \"%s\" does not exist"),
296 gettext_noop("table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a table"),
298 gettext_noop("Use DROP TABLE to remove a table.")},
299 {RELKIND_PARTITIONED_INDEX
,
300 ERRCODE_UNDEFINED_OBJECT
,
301 gettext_noop("index \"%s\" does not exist"),
302 gettext_noop("index \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not an index"),
304 gettext_noop("Use DROP INDEX to remove an index.")},
305 {'\0', 0, NULL
, NULL
, NULL
, NULL
}
308 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
309 struct DropRelationCallbackState
311 /* These fields are set by RemoveRelations: */
312 char expected_relkind
;
313 LOCKMODE heap_lockmode
;
314 /* These fields are state to track which subsidiary locks are held: */
317 /* These fields are passed back by RangeVarCallbackForDropRelation: */
319 char actual_relpersistence
;
322 /* Alter table target-type flags for ATSimplePermissions */
323 #define ATT_TABLE 0x0001
324 #define ATT_VIEW 0x0002
325 #define ATT_MATVIEW 0x0004
326 #define ATT_INDEX 0x0008
327 #define ATT_COMPOSITE_TYPE 0x0010
328 #define ATT_FOREIGN_TABLE 0x0020
329 #define ATT_PARTITIONED_INDEX 0x0040
330 #define ATT_SEQUENCE 0x0080
333 * ForeignTruncateInfo
335 * Information related to truncation of foreign tables. This is used for
336 * the elements in a hash table. It uses the server OID as lookup key,
337 * and includes a per-server list of all foreign tables involved in the
340 typedef struct ForeignTruncateInfo
344 } ForeignTruncateInfo
;
347 * Partition tables are expected to be dropped when the parent partitioned
348 * table gets dropped. Hence for partitioning we use AUTO dependency.
349 * Otherwise, for regular inheritance use NORMAL dependency.
351 #define child_dependency_type(child_is_partition) \
352 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
354 static void truncate_check_rel(Oid relid
, Form_pg_class reltuple
);
355 static void truncate_check_perms(Oid relid
, Form_pg_class reltuple
);
356 static void truncate_check_activity(Relation rel
);
357 static void RangeVarCallbackForTruncate(const RangeVar
*relation
,
358 Oid relId
, Oid oldRelId
, void *arg
);
359 static List
*MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
360 bool is_partition
, List
**supconstr
,
362 static List
*MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
);
363 static void MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
);
364 static ColumnDef
*MergeInheritedAttribute(List
*inh_columns
, int exist_attno
, const ColumnDef
*newdef
);
365 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
);
366 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
367 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
368 bool child_is_partition
);
369 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
370 int32 seqNumber
, Relation inhRelation
,
371 bool child_is_partition
);
372 static int findAttrByName(const char *attributeName
, const List
*columns
);
373 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
374 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
375 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
376 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
378 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
379 bool recurse
, bool recursing
, LOCKMODE lockmode
);
380 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
381 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
383 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
384 Relation rel
, char *constrName
,
385 bool recurse
, bool recursing
, LOCKMODE lockmode
);
386 static int transformColumnNameList(Oid relId
, List
*colList
,
387 int16
*attnums
, Oid
*atttypids
);
388 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
390 int16
*attnums
, Oid
*atttypids
,
391 Oid
*opclasses
, bool *pk_has_without_overlaps
);
392 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
393 int numattrs
, int16
*attnums
,
394 bool with_period
, Oid
*opclasses
,
395 bool *pk_has_without_overlaps
);
396 static void checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
);
397 static CoercionPathType
findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
,
399 static void validateForeignKeyConstraint(char *conname
,
400 Relation rel
, Relation pkrel
,
401 Oid pkindOid
, Oid constraintOid
, bool hasperiod
);
402 static void ATController(AlterTableStmt
*parsetree
,
403 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
404 AlterTableUtilityContext
*context
);
405 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
406 bool recurse
, bool recursing
, LOCKMODE lockmode
,
407 AlterTableUtilityContext
*context
);
408 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
409 AlterTableUtilityContext
*context
);
410 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
411 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
412 AlterTableUtilityContext
*context
);
413 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
414 Relation rel
, AlterTableCmd
*cmd
,
415 bool recurse
, LOCKMODE lockmode
,
416 AlterTablePass cur_pass
,
417 AlterTableUtilityContext
*context
);
418 static void ATRewriteTables(AlterTableStmt
*parsetree
,
419 List
**wqueue
, LOCKMODE lockmode
,
420 AlterTableUtilityContext
*context
);
421 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
);
422 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
423 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
424 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
425 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
426 AlterTableUtilityContext
*context
);
427 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
428 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
430 AlterTableUtilityContext
*context
);
431 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
432 DropBehavior behavior
);
433 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
434 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
435 AlterTableUtilityContext
*context
);
436 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
437 Relation rel
, AlterTableCmd
**cmd
,
438 bool recurse
, bool recursing
,
439 LOCKMODE lockmode
, AlterTablePass cur_pass
,
440 AlterTableUtilityContext
*context
);
441 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
443 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
444 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
445 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
447 static bool set_attnotnull(List
**wqueue
, Relation rel
,
448 AttrNumber attnum
, bool recurse
, LOCKMODE lockmode
);
449 static ObjectAddress
ATExecSetNotNull(List
**wqueue
, Relation rel
,
450 char *constrname
, char *colName
,
451 bool recurse
, bool recursing
,
452 List
**readyRels
, LOCKMODE lockmode
);
453 static ObjectAddress
ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
454 const char *colName
, LOCKMODE lockmode
);
455 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
456 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
457 List
*testConstraint
, List
*provenConstraint
);
458 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
459 Node
*newDefault
, LOCKMODE lockmode
);
460 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
462 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
463 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
464 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
465 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
466 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
467 bool recurse
, bool recursing
);
468 static ObjectAddress
ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
469 Node
*newExpr
, LOCKMODE lockmode
);
470 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
471 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
472 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
473 Node
*newValue
, LOCKMODE lockmode
);
474 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
475 Node
*options
, bool isReset
, LOCKMODE lockmode
);
476 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
477 Node
*newValue
, LOCKMODE lockmode
);
478 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
479 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
480 AlterTableUtilityContext
*context
);
481 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
482 DropBehavior behavior
,
483 bool recurse
, bool recursing
,
484 bool missing_ok
, LOCKMODE lockmode
,
485 ObjectAddresses
*addrs
);
486 static void ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
487 LOCKMODE lockmode
, AlterTableUtilityContext
*context
);
488 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
489 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
490 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
491 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
492 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
493 AlteredTableInfo
*tab
, Relation rel
,
494 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
496 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
497 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
498 IndexStmt
*stmt
, LOCKMODE lockmode
);
499 static ObjectAddress
ATAddCheckNNConstraint(List
**wqueue
,
500 AlteredTableInfo
*tab
, Relation rel
,
502 bool recurse
, bool recursing
, bool is_readd
,
504 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
505 Relation rel
, Constraint
*fkconstraint
,
506 bool recurse
, bool recursing
,
508 static ObjectAddress
addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
,
509 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
510 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
511 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
512 int numfkdelsetcols
, int16
*fkdelsetcols
,
514 Oid parentDelTrigger
, Oid parentUpdTrigger
,
516 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
517 int numfksetcols
, const int16
*fksetcolsattnums
,
519 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
520 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
521 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
522 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
523 int numfkdelsetcols
, int16
*fkdelsetcols
,
524 bool old_check_ok
, LOCKMODE lockmode
,
525 Oid parentInsTrigger
, Oid parentUpdTrigger
,
528 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
529 Relation partitionRel
);
530 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
531 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
533 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
534 Constraint
*fkconstraint
, Oid constraintOid
,
536 Oid parentInsTrigger
, Oid parentUpdTrigger
,
537 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
538 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
539 Constraint
*fkconstraint
, Oid constraintOid
,
541 Oid parentDelTrigger
, Oid parentUpdTrigger
,
542 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
543 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
545 Oid parentConstrOid
, int numfks
,
546 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
548 Oid parentInsTrigger
,
549 Oid parentUpdTrigger
,
551 static void GetForeignKeyActionTriggers(Relation trigrel
,
552 Oid conoid
, Oid confrelid
, Oid conrelid
,
553 Oid
*deleteTriggerOid
,
554 Oid
*updateTriggerOid
);
555 static void GetForeignKeyCheckTriggers(Relation trigrel
,
556 Oid conoid
, Oid confrelid
, Oid conrelid
,
557 Oid
*insertTriggerOid
,
558 Oid
*updateTriggerOid
);
559 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
560 DropBehavior behavior
, bool recurse
,
561 bool missing_ok
, LOCKMODE lockmode
);
562 static ObjectAddress
dropconstraint_internal(Relation rel
,
563 HeapTuple constraintTup
, DropBehavior behavior
,
564 bool recurse
, bool recursing
,
565 bool missing_ok
, List
**readyRels
,
567 static void ATPrepAlterColumnType(List
**wqueue
,
568 AlteredTableInfo
*tab
, Relation rel
,
569 bool recurse
, bool recursing
,
570 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
571 AlterTableUtilityContext
*context
);
572 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
573 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
574 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
575 static void RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
576 Relation rel
, AttrNumber attnum
, const char *colName
);
577 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
578 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
579 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
580 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
582 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
583 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
585 static void RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
,
586 Oid objid
, Relation rel
, List
*domname
,
587 const char *conname
);
588 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
589 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
590 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
591 List
*options
, LOCKMODE lockmode
);
592 static void change_owner_fix_column_acls(Oid relationOid
,
593 Oid oldOwnerId
, Oid newOwnerId
);
594 static void change_owner_recurse_to_sequences(Oid relationOid
,
595 Oid newOwnerId
, LOCKMODE lockmode
);
596 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
598 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
599 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
600 static void ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethod
);
601 static bool ATPrepChangePersistence(Relation rel
, bool toLogged
);
602 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
603 const char *tablespacename
, LOCKMODE lockmode
);
604 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
605 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
606 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
607 AlterTableType operation
,
609 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
610 char fires_when
, bool skip_system
, bool recurse
,
612 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
613 char fires_when
, LOCKMODE lockmode
);
614 static void ATPrepAddInherit(Relation child_rel
);
615 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
616 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
617 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
618 DependencyType deptype
);
619 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
620 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
621 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
622 static void ATExecGenericOptions(Relation rel
, List
*options
);
623 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
624 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
625 static ObjectAddress
ATExecSetCompression(Relation rel
,
626 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
628 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
629 static const char *storage_name(char c
);
631 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
632 Oid oldRelOid
, void *arg
);
633 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
634 Oid oldrelid
, void *arg
);
635 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
636 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
637 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
638 PartitionStrategy strategy
);
639 static void CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
);
640 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
641 bool expect_detached
);
642 static void ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
,
644 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
646 AlterTableUtilityContext
*context
);
647 static void AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
);
648 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
649 List
*partConstraint
,
650 bool validate_default
);
651 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
652 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
653 static void DropClonedTriggersFromPartition(Oid partitionId
);
654 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
655 Relation rel
, RangeVar
*name
,
657 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
658 bool concurrent
, Oid defaultPartOid
);
659 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
660 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
662 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
663 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
664 Relation partitionTbl
);
665 static void verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partIdx
);
666 static List
*GetParentedForeignKeyRefs(Relation partition
);
667 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
668 static char GetAttributeCompression(Oid atttypid
, const char *compression
);
669 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
672 /* ----------------------------------------------------------------
674 * Creates a new relation.
676 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
677 * The other arguments are used to extend the behavior for other cases:
678 * relkind: relkind to assign to the new relation
679 * ownerId: if not InvalidOid, use this as the new relation's owner.
680 * typaddress: if not null, it's set to the pg_type entry's address.
681 * queryString: for error reporting
683 * Note that permissions checks are done against current user regardless of
684 * ownerId. A nonzero ownerId is used when someone is creating a relation
685 * "on behalf of" someone else, so we still want to see that the current user
686 * has permissions to do it.
688 * If successful, returns the address of the new relation.
689 * ----------------------------------------------------------------
692 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
693 ObjectAddress
*typaddress
, const char *queryString
)
695 char relname
[NAMEDATALEN
];
700 TupleDesc descriptor
;
702 List
*old_constraints
;
705 List
*cookedDefaults
;
711 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
713 ObjectAddress address
;
714 LOCKMODE parentLockmode
;
715 Oid accessMethodId
= InvalidOid
;
718 * Truncate relname to appropriate length (probably a waste of time, as
719 * parser should have done this already).
721 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
724 * Check consistency of arguments
726 if (stmt
->oncommit
!= ONCOMMIT_NOOP
727 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
729 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
730 errmsg("ON COMMIT can only be used on temporary tables")));
732 if (stmt
->partspec
!= NULL
)
734 if (relkind
!= RELKIND_RELATION
)
735 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
737 relkind
= RELKIND_PARTITIONED_TABLE
;
744 * Look up the namespace in which we are supposed to create the relation,
745 * check we have permission to create there, lock it against concurrent
746 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
747 * namespace is selected.
750 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
753 * Security check: disallow creating temp tables from security-restricted
754 * code. This is needed because calling code might not expect untrusted
755 * tables to appear in pg_temp at the front of its search path.
757 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
758 && InSecurityRestrictedOperation())
760 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
761 errmsg("cannot create temporary table within security-restricted operation")));
764 * Determine the lockmode to use when scanning parents. A self-exclusive
765 * lock is needed here.
767 * For regular inheritance, if two backends attempt to add children to the
768 * same parent simultaneously, and that parent has no pre-existing
769 * children, then both will attempt to update the parent's relhassubclass
770 * field, leading to a "tuple concurrently updated" error. Also, this
771 * interlocks against a concurrent ANALYZE on the parent table, which
772 * might otherwise be attempting to clear the parent's relhassubclass
773 * field, if its previous children were recently dropped.
775 * If the child table is a partition, then we instead grab an exclusive
776 * lock on the parent because its partition descriptor will be changed by
777 * addition of the new partition.
779 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
780 ShareUpdateExclusiveLock
);
782 /* Determine the list of OIDs of the parents. */
784 foreach(listptr
, stmt
->inhRelations
)
786 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
789 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
792 * Reject duplications in the list of parents.
794 if (list_member_oid(inheritOids
, parentOid
))
796 (errcode(ERRCODE_DUPLICATE_TABLE
),
797 errmsg("relation \"%s\" would be inherited from more than once",
798 get_rel_name(parentOid
))));
800 inheritOids
= lappend_oid(inheritOids
, parentOid
);
804 * Select tablespace to use: an explicitly indicated one, or (in the case
805 * of a partitioned table) the parent's, if it has one.
807 if (stmt
->tablespacename
)
809 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
811 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
813 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
814 errmsg("cannot specify default tablespace for partitioned relations")));
816 else if (stmt
->partbound
)
818 Assert(list_length(inheritOids
) == 1);
819 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
822 tablespaceId
= InvalidOid
;
824 /* still nothing? use the default */
825 if (!OidIsValid(tablespaceId
))
826 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
829 /* Check permissions except when using database's default */
830 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
834 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
836 if (aclresult
!= ACLCHECK_OK
)
837 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
838 get_tablespace_name(tablespaceId
));
841 /* In all cases disallow placing user relations in pg_global */
842 if (tablespaceId
== GLOBALTABLESPACE_OID
)
844 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
845 errmsg("only shared relations can be placed in pg_global tablespace")));
847 /* Identify user ID that will own the table */
848 if (!OidIsValid(ownerId
))
849 ownerId
= GetUserId();
852 * Parse and validate reloptions, if any.
854 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
860 (void) view_reloptions(reloptions
, true);
862 case RELKIND_PARTITIONED_TABLE
:
863 (void) partitioned_table_reloptions(reloptions
, true);
866 (void) heap_reloptions(relkind
, reloptions
, true);
869 if (stmt
->ofTypename
)
873 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
875 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
876 if (aclresult
!= ACLCHECK_OK
)
877 aclcheck_error_type(aclresult
, ofTypeId
);
880 ofTypeId
= InvalidOid
;
883 * Look up inheritance ancestors and generate relation schema, including
884 * inherited attributes. (Note that stmt->tableElts is destructively
885 * modified by MergeAttributes.)
888 MergeAttributes(stmt
->tableElts
, inheritOids
,
889 stmt
->relation
->relpersistence
,
890 stmt
->partbound
!= NULL
,
891 &old_constraints
, &old_notnulls
);
894 * Create a tuple descriptor from the relation schema. Note that this
895 * deals with column names, types, and in-descriptor NOT NULL flags, but
896 * not default values, NOT NULL or CHECK constraints; we handle those
899 descriptor
= BuildDescForRelation(stmt
->tableElts
);
902 * Find columns with default values and prepare for insertion of the
903 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
904 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
905 * while raw defaults go into a list of RawColumnDefault structs that will
906 * be processed by AddRelationNewConstraints. (We can't deal with raw
907 * expressions until we can do transformExpr.)
909 * We can set the atthasdef flags now in the tuple descriptor; this just
910 * saves StoreAttrDefault from having to do an immediate update of the
914 cookedDefaults
= NIL
;
917 foreach(listptr
, stmt
->tableElts
)
919 ColumnDef
*colDef
= lfirst(listptr
);
920 Form_pg_attribute attr
;
923 attr
= TupleDescAttr(descriptor
, attnum
- 1);
925 if (colDef
->raw_default
!= NULL
)
927 RawColumnDefault
*rawEnt
;
929 Assert(colDef
->cooked_default
== NULL
);
931 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
932 rawEnt
->attnum
= attnum
;
933 rawEnt
->raw_default
= colDef
->raw_default
;
934 rawEnt
->missingMode
= false;
935 rawEnt
->generated
= colDef
->generated
;
936 rawDefaults
= lappend(rawDefaults
, rawEnt
);
937 attr
->atthasdef
= true;
939 else if (colDef
->cooked_default
!= NULL
)
941 CookedConstraint
*cooked
;
943 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
944 cooked
->contype
= CONSTR_DEFAULT
;
945 cooked
->conoid
= InvalidOid
; /* until created */
947 cooked
->attnum
= attnum
;
948 cooked
->expr
= colDef
->cooked_default
;
949 cooked
->skip_validation
= false;
950 cooked
->is_local
= true; /* not used for defaults */
951 cooked
->inhcount
= 0; /* ditto */
952 cooked
->is_no_inherit
= false;
953 cookedDefaults
= lappend(cookedDefaults
, cooked
);
954 attr
->atthasdef
= true;
959 * Select access method to use: an explicitly indicated one, or (in the
960 * case of a partitioned table) the parent's, if it has one.
962 if (stmt
->accessMethod
!= NULL
)
963 accessMethodId
= get_table_am_oid(stmt
->accessMethod
, false);
964 else if (stmt
->partbound
)
966 Assert(list_length(inheritOids
) == 1);
967 accessMethodId
= get_rel_relam(linitial_oid(inheritOids
));
970 accessMethodId
= InvalidOid
;
972 /* still nothing? use the default */
973 if (RELKIND_HAS_TABLE_AM(relkind
) && !OidIsValid(accessMethodId
))
974 accessMethodId
= get_table_am_oid(default_table_access_method
, false);
977 * Create the relation. Inherited defaults and constraints are passed in
978 * for immediate handling --- since they don't need parsing, they can be
979 * stored immediately.
981 relationId
= heap_create_with_catalog(relname
,
990 list_concat(cookedDefaults
,
993 stmt
->relation
->relpersistence
,
999 allowSystemTableMods
,
1005 * We must bump the command counter to make the newly-created relation
1006 * tuple visible for opening.
1008 CommandCounterIncrement();
1011 * Open the new relation and acquire exclusive lock on it. This isn't
1012 * really necessary for locking out other backends (since they can't see
1013 * the new rel anyway until we commit), but it keeps the lock manager from
1014 * complaining about deadlock risks.
1016 rel
= relation_open(relationId
, AccessExclusiveLock
);
1019 * Now add any newly specified column default and generation expressions
1020 * to the new relation. These are passed to us in the form of raw
1021 * parsetrees; we need to transform them to executable expression trees
1022 * before they can be added. The most convenient way to do that is to
1023 * apply the parser's transformExpr routine, but transformExpr doesn't
1024 * work unless we have a pre-existing relation. So, the transformation has
1025 * to be postponed to this final step of CREATE TABLE.
1027 * This needs to be before processing the partitioning clauses because
1028 * those could refer to generated columns.
1031 AddRelationNewConstraints(rel
, rawDefaults
, NIL
,
1032 true, true, false, queryString
);
1035 * Make column generation expressions visible for use by partitioning.
1037 CommandCounterIncrement();
1039 /* Process and store partition bound, if any. */
1040 if (stmt
->partbound
)
1042 PartitionBoundSpec
*bound
;
1044 Oid parentId
= linitial_oid(inheritOids
),
1048 ParseNamespaceItem
*nsitem
;
1050 /* Already have strong enough lock on the parent */
1051 parent
= table_open(parentId
, NoLock
);
1054 * We are going to try to validate the partition bound specification
1055 * against the partition key of parentRel, so it better have one.
1057 if (parent
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
1059 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1060 errmsg("\"%s\" is not partitioned",
1061 RelationGetRelationName(parent
))));
1064 * The partition constraint of the default partition depends on the
1065 * partition bounds of every other partition. It is possible that
1066 * another backend might be about to execute a query on the default
1067 * partition table, and that the query relies on previously cached
1068 * default partition constraints. We must therefore take a table lock
1069 * strong enough to prevent all queries on the default partition from
1070 * proceeding until we commit and send out a shared-cache-inval notice
1071 * that will make them update their index lists.
1073 * Order of locking: The relation being added won't be visible to
1074 * other backends until it is committed, hence here in
1075 * DefineRelation() the order of locking the default partition and the
1076 * relation being added does not matter. But at all other places we
1077 * need to lock the default relation before we lock the relation being
1078 * added or removed i.e. we should take the lock in same order at all
1079 * the places such that lock parent, lock default partition and then
1080 * lock the partition so as to avoid a deadlock.
1083 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent
,
1085 if (OidIsValid(defaultPartOid
))
1086 defaultRel
= table_open(defaultPartOid
, AccessExclusiveLock
);
1088 /* Transform the bound values */
1089 pstate
= make_parsestate(NULL
);
1090 pstate
->p_sourcetext
= queryString
;
1093 * Add an nsitem containing this relation, so that transformExpr
1094 * called on partition bound expressions is able to report errors
1095 * using a proper context.
1097 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
1098 NULL
, false, false);
1099 addNSItemToQuery(pstate
, nsitem
, false, true, true);
1101 bound
= transformPartitionBound(pstate
, parent
, stmt
->partbound
);
1104 * Check first that the new partition's bound is valid and does not
1105 * overlap with any of existing partitions of the parent.
1107 check_new_partition_bound(relname
, parent
, bound
, pstate
);
1110 * If the default partition exists, its partition constraints will
1111 * change after the addition of this new partition such that it won't
1112 * allow any row that qualifies for this new partition. So, check that
1113 * the existing data in the default partition satisfies the constraint
1114 * as it will exist after adding this partition.
1116 if (OidIsValid(defaultPartOid
))
1118 check_default_partition_contents(parent
, defaultRel
, bound
);
1119 /* Keep the lock until commit. */
1120 table_close(defaultRel
, NoLock
);
1123 /* Update the pg_class entry. */
1124 StorePartitionBound(rel
, parent
, bound
);
1126 table_close(parent
, NoLock
);
1129 /* Store inheritance information for new rel. */
1130 StoreCatalogInheritance(relationId
, inheritOids
, stmt
->partbound
!= NULL
);
1133 * Process the partitioning specification (if any) and store the partition
1134 * key information into the catalog.
1140 AttrNumber partattrs
[PARTITION_MAX_KEYS
];
1141 Oid partopclass
[PARTITION_MAX_KEYS
];
1142 Oid partcollation
[PARTITION_MAX_KEYS
];
1143 List
*partexprs
= NIL
;
1145 pstate
= make_parsestate(NULL
);
1146 pstate
->p_sourcetext
= queryString
;
1148 partnatts
= list_length(stmt
->partspec
->partParams
);
1150 /* Protect fixed-size arrays here and in executor */
1151 if (partnatts
> PARTITION_MAX_KEYS
)
1153 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
1154 errmsg("cannot partition using more than %d columns",
1155 PARTITION_MAX_KEYS
)));
1158 * We need to transform the raw parsetrees corresponding to partition
1159 * expressions into executable expression trees. Like column defaults
1160 * and CHECK constraints, we could not have done the transformation
1163 stmt
->partspec
= transformPartitionSpec(rel
, stmt
->partspec
);
1165 ComputePartitionAttrs(pstate
, rel
, stmt
->partspec
->partParams
,
1166 partattrs
, &partexprs
, partopclass
,
1167 partcollation
, stmt
->partspec
->strategy
);
1169 StorePartitionKey(rel
, stmt
->partspec
->strategy
, partnatts
, partattrs
,
1171 partopclass
, partcollation
);
1173 /* make it all visible */
1174 CommandCounterIncrement();
1178 * If we're creating a partition, create now all the indexes, triggers,
1179 * FKs defined in the parent.
1181 * We can't do it earlier, because DefineIndex wants to know the partition
1182 * key which we just stored.
1184 if (stmt
->partbound
)
1186 Oid parentId
= linitial_oid(inheritOids
);
1191 /* Already have strong enough lock on the parent */
1192 parent
= table_open(parentId
, NoLock
);
1193 idxlist
= RelationGetIndexList(parent
);
1196 * For each index in the parent table, create one in the partition
1198 foreach(cell
, idxlist
)
1200 Relation idxRel
= index_open(lfirst_oid(cell
), AccessShareLock
);
1205 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1207 if (idxRel
->rd_index
->indisunique
)
1209 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1210 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1211 RelationGetRelationName(parent
)),
1212 errdetail("Table \"%s\" contains indexes that are unique.",
1213 RelationGetRelationName(parent
))));
1216 index_close(idxRel
, AccessShareLock
);
1221 attmap
= build_attrmap_by_name(RelationGetDescr(rel
),
1222 RelationGetDescr(parent
),
1225 generateClonedIndexStmt(NULL
, idxRel
,
1226 attmap
, &constraintOid
);
1227 DefineIndex(RelationGetRelid(rel
),
1230 RelationGetRelid(idxRel
),
1233 false, false, false, false, false);
1235 index_close(idxRel
, AccessShareLock
);
1241 * If there are any row-level triggers, clone them to the new
1244 if (parent
->trigdesc
!= NULL
)
1245 CloneRowTriggersToPartition(parent
, rel
);
1248 * And foreign keys too. Note that because we're freshly creating the
1249 * table, there is no need to verify these new constraints.
1251 CloneForeignKeyConstraints(NULL
, parent
, rel
);
1253 table_close(parent
, NoLock
);
1257 * Now add any newly specified CHECK constraints to the new relation. Same
1258 * as for defaults above, but these need to come after partitioning is set
1261 if (stmt
->constraints
)
1262 AddRelationNewConstraints(rel
, NIL
, stmt
->constraints
,
1263 true, true, false, queryString
);
1266 * Finally, merge the not-null constraints that are declared directly with
1267 * those that come from parent relations (making sure to count inheritance
1268 * appropriately for each), create them, and set the attnotnull flag on
1269 * columns that don't yet have it.
1271 nncols
= AddRelationNotNullConstraints(rel
, stmt
->nnconstraints
,
1273 foreach(listptr
, nncols
)
1274 set_attnotnull(NULL
, rel
, lfirst_int(listptr
), false, NoLock
);
1276 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1279 * Clean up. We keep lock on new relation (although it shouldn't be
1280 * visible to anyone else anyway, until commit).
1282 relation_close(rel
, NoLock
);
1288 * BuildDescForRelation
1290 * Given a list of ColumnDef nodes, build a TupleDesc.
1292 * Note: tdtypeid will need to be filled in later on.
1295 BuildDescForRelation(const List
*columns
)
1309 * allocate a new tuple descriptor
1311 natts
= list_length(columns
);
1312 desc
= CreateTemplateTupleDesc(natts
);
1313 has_not_null
= false;
1319 ColumnDef
*entry
= lfirst(l
);
1320 AclResult aclresult
;
1321 Form_pg_attribute att
;
1324 * for each entry in the list, get the name and type information from
1325 * the list and have TupleDescInitEntry fill in the attribute
1326 * information we need.
1330 attname
= entry
->colname
;
1331 typenameTypeIdAndMod(NULL
, entry
->typeName
, &atttypid
, &atttypmod
);
1333 aclresult
= object_aclcheck(TypeRelationId
, atttypid
, GetUserId(), ACL_USAGE
);
1334 if (aclresult
!= ACLCHECK_OK
)
1335 aclcheck_error_type(aclresult
, atttypid
);
1337 attcollation
= GetColumnDefCollation(NULL
, entry
, atttypid
);
1338 attdim
= list_length(entry
->typeName
->arrayBounds
);
1339 if (attdim
> PG_INT16_MAX
)
1341 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1342 errmsg("too many array dimensions"));
1344 if (entry
->typeName
->setof
)
1346 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
1347 errmsg("column \"%s\" cannot be declared SETOF",
1350 TupleDescInitEntry(desc
, attnum
, attname
,
1351 atttypid
, atttypmod
, attdim
);
1352 att
= TupleDescAttr(desc
, attnum
- 1);
1354 /* Override TupleDescInitEntry's settings as requested */
1355 TupleDescInitEntryCollation(desc
, attnum
, attcollation
);
1357 /* Fill in additional stuff not handled by TupleDescInitEntry */
1358 att
->attnotnull
= entry
->is_not_null
;
1359 has_not_null
|= entry
->is_not_null
;
1360 att
->attislocal
= entry
->is_local
;
1361 att
->attinhcount
= entry
->inhcount
;
1362 att
->attidentity
= entry
->identity
;
1363 att
->attgenerated
= entry
->generated
;
1364 att
->attcompression
= GetAttributeCompression(att
->atttypid
, entry
->compression
);
1366 att
->attstorage
= entry
->storage
;
1367 else if (entry
->storage_name
)
1368 att
->attstorage
= GetAttributeStorage(att
->atttypid
, entry
->storage_name
);
1373 TupleConstr
*constr
= (TupleConstr
*) palloc0(sizeof(TupleConstr
));
1375 constr
->has_not_null
= true;
1376 constr
->has_generated_stored
= false;
1377 constr
->defval
= NULL
;
1378 constr
->missing
= NULL
;
1379 constr
->num_defval
= 0;
1380 constr
->check
= NULL
;
1381 constr
->num_check
= 0;
1382 desc
->constr
= constr
;
1386 desc
->constr
= NULL
;
1393 * Emit the right error or warning message for a "DROP" command issued on a
1394 * non-existent relation
1397 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1399 const struct dropmsgstrings
*rentry
;
1401 if (rel
->schemaname
!= NULL
&&
1402 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1407 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1408 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1413 (errmsg("schema \"%s\" does not exist, skipping",
1419 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1421 if (rentry
->kind
== rightkind
)
1426 (errcode(rentry
->nonexistent_code
),
1427 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1431 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1437 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1441 * Emit the right error message for a "DROP" command issued on a
1442 * relation of the wrong type
1445 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1447 const struct dropmsgstrings
*rentry
;
1448 const struct dropmsgstrings
*wentry
;
1450 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1451 if (rentry
->kind
== rightkind
)
1453 Assert(rentry
->kind
!= '\0');
1455 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1456 if (wentry
->kind
== wrongkind
)
1458 /* wrongkind could be something we don't have in our table... */
1461 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1462 errmsg(rentry
->nota_msg
, relname
),
1463 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1468 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1469 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1472 RemoveRelations(DropStmt
*drop
)
1474 ObjectAddresses
*objects
;
1478 LOCKMODE lockmode
= AccessExclusiveLock
;
1480 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1481 if (drop
->concurrent
)
1484 * Note that for temporary relations this lock may get upgraded later
1485 * on, but as no other session can access a temporary relation, this
1488 lockmode
= ShareUpdateExclusiveLock
;
1489 Assert(drop
->removeType
== OBJECT_INDEX
);
1490 if (list_length(drop
->objects
) != 1)
1492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1493 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1494 if (drop
->behavior
== DROP_CASCADE
)
1496 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1497 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1501 * First we identify all the relations, then we delete them in a single
1502 * performMultipleDeletions() call. This is to avoid unwanted DROP
1503 * RESTRICT errors if one of the relations depends on another.
1506 /* Determine required relkind */
1507 switch (drop
->removeType
)
1510 relkind
= RELKIND_RELATION
;
1514 relkind
= RELKIND_INDEX
;
1517 case OBJECT_SEQUENCE
:
1518 relkind
= RELKIND_SEQUENCE
;
1522 relkind
= RELKIND_VIEW
;
1525 case OBJECT_MATVIEW
:
1526 relkind
= RELKIND_MATVIEW
;
1529 case OBJECT_FOREIGN_TABLE
:
1530 relkind
= RELKIND_FOREIGN_TABLE
;
1534 elog(ERROR
, "unrecognized drop object type: %d",
1535 (int) drop
->removeType
);
1536 relkind
= 0; /* keep compiler quiet */
1540 /* Lock and validate each relation; build a list of object addresses */
1541 objects
= new_object_addresses();
1543 foreach(cell
, drop
->objects
)
1545 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1548 struct DropRelationCallbackState state
;
1551 * These next few steps are a great deal like relation_openrv, but we
1552 * don't bother building a relcache entry since we don't need it.
1554 * Check for shared-cache-inval messages before trying to access the
1555 * relation. This is needed to cover the case where the name
1556 * identifies a rel that has been dropped and recreated since the
1557 * start of our transaction: if we don't flush the old syscache entry,
1558 * then we'll latch onto that entry and suffer an error later.
1560 AcceptInvalidationMessages();
1562 /* Look up the appropriate relation using namespace search. */
1563 state
.expected_relkind
= relkind
;
1564 state
.heap_lockmode
= drop
->concurrent
?
1565 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1566 /* We must initialize these fields to show that no locks are held: */
1567 state
.heapOid
= InvalidOid
;
1568 state
.partParentOid
= InvalidOid
;
1570 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1571 RangeVarCallbackForDropRelation
,
1575 if (!OidIsValid(relOid
))
1577 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1582 * Decide if concurrent mode needs to be used here or not. The
1583 * callback retrieved the rel's persistence for us.
1585 if (drop
->concurrent
&&
1586 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1588 Assert(list_length(drop
->objects
) == 1 &&
1589 drop
->removeType
== OBJECT_INDEX
);
1590 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1594 * Concurrent index drop cannot be used with partitioned indexes,
1597 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1598 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1601 errmsg("cannot drop partitioned index \"%s\" concurrently",
1605 * If we're told to drop a partitioned index, we must acquire lock on
1606 * all the children of its parent partitioned table before proceeding.
1607 * Otherwise we'd try to lock the child index partitions before their
1608 * tables, leading to potential deadlock against other sessions that
1609 * will lock those objects in the other order.
1611 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1612 (void) find_all_inheritors(state
.heapOid
,
1613 state
.heap_lockmode
,
1616 /* OK, we're ready to delete this one */
1617 obj
.classId
= RelationRelationId
;
1618 obj
.objectId
= relOid
;
1619 obj
.objectSubId
= 0;
1621 add_exact_object_address(&obj
, objects
);
1624 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1626 free_object_addresses(objects
);
1630 * Before acquiring a table lock, check whether we have sufficient rights.
1631 * In the case of DROP INDEX, also try to lock the table before the index.
1632 * Also, if the table to be dropped is a partition, we try to lock the parent
1636 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1640 struct DropRelationCallbackState
*state
;
1641 char expected_relkind
;
1643 Form_pg_class classform
;
1644 LOCKMODE heap_lockmode
;
1645 bool invalid_system_index
= false;
1647 state
= (struct DropRelationCallbackState
*) arg
;
1648 heap_lockmode
= state
->heap_lockmode
;
1651 * If we previously locked some other index's heap, and the name we're
1652 * looking up no longer refers to that relation, release the now-useless
1655 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1657 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1658 state
->heapOid
= InvalidOid
;
1662 * Similarly, if we previously locked some other partition's heap, and the
1663 * name we're looking up no longer refers to that relation, release the
1666 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1668 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1669 state
->partParentOid
= InvalidOid
;
1672 /* Didn't find a relation, so no need for locking or permission checks. */
1673 if (!OidIsValid(relOid
))
1676 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1677 if (!HeapTupleIsValid(tuple
))
1678 return; /* concurrently dropped, so nothing to do */
1679 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1680 is_partition
= classform
->relispartition
;
1682 /* Pass back some data to save lookups in RemoveRelations */
1683 state
->actual_relkind
= classform
->relkind
;
1684 state
->actual_relpersistence
= classform
->relpersistence
;
1687 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1688 * but RemoveRelations() can only pass one relkind for a given relation.
1689 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1690 * That means we must be careful before giving the wrong type error when
1691 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1692 * exists with indexes.
1694 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1695 expected_relkind
= RELKIND_RELATION
;
1696 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1697 expected_relkind
= RELKIND_INDEX
;
1699 expected_relkind
= classform
->relkind
;
1701 if (state
->expected_relkind
!= expected_relkind
)
1702 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1703 state
->expected_relkind
);
1705 /* Allow DROP to either table owner or schema owner */
1706 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1707 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1708 aclcheck_error(ACLCHECK_NOT_OWNER
,
1709 get_relkind_objtype(classform
->relkind
),
1713 * Check the case of a system index that might have been invalidated by a
1714 * failed concurrent process and allow its drop. For the time being, this
1715 * only concerns indexes of toast relations that became invalid during a
1716 * REINDEX CONCURRENTLY process.
1718 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1721 Form_pg_index indexform
;
1724 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1725 if (!HeapTupleIsValid(locTuple
))
1727 ReleaseSysCache(tuple
);
1731 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1732 indisvalid
= indexform
->indisvalid
;
1733 ReleaseSysCache(locTuple
);
1735 /* Mark object as being an invalid index of system catalogs */
1737 invalid_system_index
= true;
1740 /* In the case of an invalid index, it is fine to bypass this check */
1741 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1743 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1744 errmsg("permission denied: \"%s\" is a system catalog",
1747 ReleaseSysCache(tuple
);
1750 * In DROP INDEX, attempt to acquire lock on the parent table before
1751 * locking the index. index_drop() will need this anyway, and since
1752 * regular queries lock tables before their indexes, we risk deadlock if
1753 * we do it the other way around. No error if we don't find a pg_index
1754 * entry, though --- the relation may have been dropped. Note that this
1755 * code will execute for either plain or partitioned indexes.
1757 if (expected_relkind
== RELKIND_INDEX
&&
1758 relOid
!= oldRelOid
)
1760 state
->heapOid
= IndexGetRelation(relOid
, true);
1761 if (OidIsValid(state
->heapOid
))
1762 LockRelationOid(state
->heapOid
, heap_lockmode
);
1766 * Similarly, if the relation is a partition, we must acquire lock on its
1767 * parent before locking the partition. That's because queries lock the
1768 * parent before its partitions, so we risk deadlock if we do it the other
1771 if (is_partition
&& relOid
!= oldRelOid
)
1773 state
->partParentOid
= get_partition_parent(relOid
, true);
1774 if (OidIsValid(state
->partParentOid
))
1775 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1781 * Executes a TRUNCATE command.
1783 * This is a multi-relation truncate. We first open and grab exclusive
1784 * lock on all relations involved, checking permissions and otherwise
1785 * verifying that the relation is OK for truncation. Note that if relations
1786 * are foreign tables, at this stage, we have not yet checked that their
1787 * foreign data in external data sources are OK for truncation. These are
1788 * checked when foreign data are actually truncated later. In CASCADE mode,
1789 * relations having FK references to the targeted relations are automatically
1790 * added to the group; in RESTRICT mode, we check that all FK references are
1791 * internal to the group that's being truncated. Finally all the relations
1792 * are truncated and reindexed.
1795 ExecuteTruncate(TruncateStmt
*stmt
)
1799 List
*relids_logged
= NIL
;
1803 * Open, exclusive-lock, and check all the explicitly-specified relations
1805 foreach(cell
, stmt
->relations
)
1807 RangeVar
*rv
= lfirst(cell
);
1809 bool recurse
= rv
->inh
;
1811 LOCKMODE lockmode
= AccessExclusiveLock
;
1813 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1814 0, RangeVarCallbackForTruncate
,
1817 /* don't throw error for "TRUNCATE foo, foo" */
1818 if (list_member_oid(relids
, myrelid
))
1821 /* open the relation, we already hold a lock on it */
1822 rel
= table_open(myrelid
, NoLock
);
1825 * RangeVarGetRelidExtended() has done most checks with its callback,
1826 * but other checks with the now-opened Relation remain.
1828 truncate_check_activity(rel
);
1830 rels
= lappend(rels
, rel
);
1831 relids
= lappend_oid(relids
, myrelid
);
1833 /* Log this relation only if needed for logical decoding */
1834 if (RelationIsLogicallyLogged(rel
))
1835 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1842 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1844 foreach(child
, children
)
1846 Oid childrelid
= lfirst_oid(child
);
1848 if (list_member_oid(relids
, childrelid
))
1851 /* find_all_inheritors already got lock */
1852 rel
= table_open(childrelid
, NoLock
);
1855 * It is possible that the parent table has children that are
1856 * temp tables of other backends. We cannot safely access
1857 * such tables (because of buffering issues), and the best
1858 * thing to do is to silently ignore them. Note that this
1859 * check is the same as one of the checks done in
1860 * truncate_check_activity() called below, still it is kept
1861 * here for simplicity.
1863 if (RELATION_IS_OTHER_TEMP(rel
))
1865 table_close(rel
, lockmode
);
1870 * Inherited TRUNCATE commands perform access permission
1871 * checks on the parent table only. So we skip checking the
1872 * children's permissions and don't call
1873 * truncate_check_perms() here.
1875 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1876 truncate_check_activity(rel
);
1878 rels
= lappend(rels
, rel
);
1879 relids
= lappend_oid(relids
, childrelid
);
1881 /* Log this relation only if needed for logical decoding */
1882 if (RelationIsLogicallyLogged(rel
))
1883 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1886 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1888 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1889 errmsg("cannot truncate only a partitioned table"),
1890 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1893 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1894 stmt
->behavior
, stmt
->restart_seqs
, false);
1896 /* And close the rels */
1899 Relation rel
= (Relation
) lfirst(cell
);
1901 table_close(rel
, NoLock
);
1906 * ExecuteTruncateGuts
1908 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1909 * command (see above) as well as replication subscribers that execute a
1910 * replicated TRUNCATE action.
1912 * explicit_rels is the list of Relations to truncate that the command
1913 * specified. relids is the list of Oids corresponding to explicit_rels.
1914 * relids_logged is the list of Oids (a subset of relids) that require
1915 * WAL-logging. This is all a bit redundant, but the existing callers have
1916 * this information handy in this form.
1919 ExecuteTruncateGuts(List
*explicit_rels
,
1921 List
*relids_logged
,
1922 DropBehavior behavior
, bool restart_seqs
,
1923 bool run_as_table_owner
)
1926 List
*seq_relids
= NIL
;
1927 HTAB
*ft_htab
= NULL
;
1929 ResultRelInfo
*resultRelInfos
;
1930 ResultRelInfo
*resultRelInfo
;
1931 SubTransactionId mySubid
;
1936 * Check the explicitly-specified relations.
1938 * In CASCADE mode, suck in all referencing relations as well. This
1939 * requires multiple iterations to find indirectly-dependent relations. At
1940 * each phase, we need to exclusive-lock new rels before looking for their
1941 * dependencies, else we might miss something. Also, we check each rel as
1942 * soon as we open it, to avoid a faux pas such as holding lock for a long
1943 * time on a rel we have no permissions for.
1945 rels
= list_copy(explicit_rels
);
1946 if (behavior
== DROP_CASCADE
)
1952 newrelids
= heap_truncate_find_FKs(relids
);
1953 if (newrelids
== NIL
)
1954 break; /* nothing else to add */
1956 foreach(cell
, newrelids
)
1958 Oid relid
= lfirst_oid(cell
);
1961 rel
= table_open(relid
, AccessExclusiveLock
);
1963 (errmsg("truncate cascades to table \"%s\"",
1964 RelationGetRelationName(rel
))));
1965 truncate_check_rel(relid
, rel
->rd_rel
);
1966 truncate_check_perms(relid
, rel
->rd_rel
);
1967 truncate_check_activity(rel
);
1968 rels
= lappend(rels
, rel
);
1969 relids
= lappend_oid(relids
, relid
);
1971 /* Log this relation only if needed for logical decoding */
1972 if (RelationIsLogicallyLogged(rel
))
1973 relids_logged
= lappend_oid(relids_logged
, relid
);
1979 * Check foreign key references. In CASCADE mode, this should be
1980 * unnecessary since we just pulled in all the references; but as a
1981 * cross-check, do it anyway if in an Assert-enabled build.
1983 #ifdef USE_ASSERT_CHECKING
1984 heap_truncate_check_FKs(rels
, false);
1986 if (behavior
== DROP_RESTRICT
)
1987 heap_truncate_check_FKs(rels
, false);
1991 * If we are asked to restart sequences, find all the sequences, lock them
1992 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1993 * We want to do this early since it's pointless to do all the truncation
1994 * work only to fail on sequence permissions.
2000 Relation rel
= (Relation
) lfirst(cell
);
2001 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
2004 foreach(seqcell
, seqlist
)
2006 Oid seq_relid
= lfirst_oid(seqcell
);
2009 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
2011 /* This check must match AlterSequence! */
2012 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
2013 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
2014 RelationGetRelationName(seq_rel
));
2016 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
2018 relation_close(seq_rel
, NoLock
);
2023 /* Prepare to catch AFTER triggers. */
2024 AfterTriggerBeginQuery();
2027 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2028 * each relation. We don't need to call ExecOpenIndices, though.
2030 * We put the ResultRelInfos in the es_opened_result_relations list, even
2031 * though we don't have a range table and don't populate the
2032 * es_result_relations array. That's a bit bogus, but it's enough to make
2033 * ExecGetTriggerResultRel() find them.
2035 estate
= CreateExecutorState();
2036 resultRelInfos
= (ResultRelInfo
*)
2037 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
2038 resultRelInfo
= resultRelInfos
;
2041 Relation rel
= (Relation
) lfirst(cell
);
2043 InitResultRelInfo(resultRelInfo
,
2045 0, /* dummy rangetable index */
2048 estate
->es_opened_result_relations
=
2049 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
2054 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2055 * truncating (this is because one of them might throw an error). Also, if
2056 * we were to allow them to prevent statement execution, that would need
2057 * to be handled here.
2059 resultRelInfo
= resultRelInfos
;
2064 if (run_as_table_owner
)
2065 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2067 ExecBSTruncateTriggers(estate
, resultRelInfo
);
2068 if (run_as_table_owner
)
2069 RestoreUserContext(&ucxt
);
2074 * OK, truncate each table.
2076 mySubid
= GetCurrentSubTransactionId();
2080 Relation rel
= (Relation
) lfirst(cell
);
2082 /* Skip partitioned tables as there is nothing to do */
2083 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
2087 * Build the lists of foreign tables belonging to each foreign server
2088 * and pass each list to the foreign data wrapper's callback function,
2089 * so that each server can truncate its all foreign tables in bulk.
2090 * Each list is saved as a single entry in a hash table that uses the
2091 * server OID as lookup key.
2093 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
2095 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
2097 ForeignTruncateInfo
*ft_info
;
2099 /* First time through, initialize hashtable for foreign tables */
2104 memset(&hctl
, 0, sizeof(HASHCTL
));
2105 hctl
.keysize
= sizeof(Oid
);
2106 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
2107 hctl
.hcxt
= CurrentMemoryContext
;
2109 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
2110 32, /* start small and extend */
2112 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
2115 /* Find or create cached entry for the foreign table */
2116 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
2118 ft_info
->rels
= NIL
;
2121 * Save the foreign table in the entry of the server that the
2122 * foreign table belongs to.
2124 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2129 * Normally, we need a transaction-safe truncation here. However, if
2130 * the table was either created in the current (sub)transaction or has
2131 * a new relfilenumber in the current (sub)transaction, then we can
2132 * just truncate it in-place, because a rollback would cause the whole
2133 * table or the current physical file to be thrown away anyway.
2135 if (rel
->rd_createSubid
== mySubid
||
2136 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2138 /* Immediate, non-rollbackable truncation is OK */
2139 heap_truncate_one_rel(rel
);
2145 ReindexParams reindex_params
= {0};
2148 * This effectively deletes all rows in the table, and may be done
2149 * in a serializable transaction. In that case we must record a
2150 * rw-conflict in to this transaction from each transaction
2151 * holding a predicate lock on the table.
2153 CheckTableForSerializableConflictIn(rel
);
2156 * Need the full transaction-safe pushups.
2158 * Create a new empty storage file for the relation, and assign it
2159 * as the relfilenumber value. The old storage file is scheduled
2160 * for deletion at commit.
2162 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2164 heap_relid
= RelationGetRelid(rel
);
2167 * The same for the toast table, if any.
2169 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2170 if (OidIsValid(toast_relid
))
2172 Relation toastrel
= relation_open(toast_relid
,
2173 AccessExclusiveLock
);
2175 RelationSetNewRelfilenumber(toastrel
,
2176 toastrel
->rd_rel
->relpersistence
);
2177 table_close(toastrel
, NoLock
);
2181 * Reconstruct the indexes to match, and we're done.
2183 reindex_relation(NULL
, heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2187 pgstat_count_truncate(rel
);
2190 /* Now go through the hash table, and truncate foreign tables */
2193 ForeignTruncateInfo
*ft_info
;
2194 HASH_SEQ_STATUS seq
;
2196 hash_seq_init(&seq
, ft_htab
);
2200 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2202 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2204 /* truncate_check_rel() has checked that already */
2205 Assert(routine
->ExecForeignTruncate
!= NULL
);
2207 routine
->ExecForeignTruncate(ft_info
->rels
,
2214 hash_destroy(ft_htab
);
2220 * Restart owned sequences if we were asked to.
2222 foreach(cell
, seq_relids
)
2224 Oid seq_relid
= lfirst_oid(cell
);
2226 ResetSequence(seq_relid
);
2230 * Write a WAL record to allow this set of actions to be logically
2233 * Assemble an array of relids so we can write a single WAL record for the
2236 if (relids_logged
!= NIL
)
2238 xl_heap_truncate xlrec
;
2241 /* should only get here if wal_level >= logical */
2242 Assert(XLogLogicalInfoActive());
2244 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2245 foreach(cell
, relids_logged
)
2246 logrelids
[i
++] = lfirst_oid(cell
);
2248 xlrec
.dbId
= MyDatabaseId
;
2249 xlrec
.nrelids
= list_length(relids_logged
);
2251 if (behavior
== DROP_CASCADE
)
2252 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2254 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2257 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2258 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2260 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2262 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2266 * Process all AFTER STATEMENT TRUNCATE triggers.
2268 resultRelInfo
= resultRelInfos
;
2273 if (run_as_table_owner
)
2274 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2276 ExecASTruncateTriggers(estate
, resultRelInfo
);
2277 if (run_as_table_owner
)
2278 RestoreUserContext(&ucxt
);
2282 /* Handle queued AFTER triggers */
2283 AfterTriggerEndQuery(estate
);
2285 /* We can clean up the EState now */
2286 FreeExecutorState(estate
);
2289 * Close any rels opened by CASCADE (can't do this while EState still
2292 rels
= list_difference_ptr(rels
, explicit_rels
);
2295 Relation rel
= (Relation
) lfirst(cell
);
2297 table_close(rel
, NoLock
);
2302 * Check that a given relation is safe to truncate. Subroutine for
2303 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2306 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2308 char *relname
= NameStr(reltuple
->relname
);
2311 * Only allow truncate on regular tables, foreign tables using foreign
2312 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2313 * latter are only being included here for the following checks; no
2314 * physical truncation will occur in their case.).
2316 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2318 Oid serverid
= GetForeignServerIdByRelId(relid
);
2319 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2321 if (!fdwroutine
->ExecForeignTruncate
)
2323 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2324 errmsg("cannot truncate foreign table \"%s\"",
2327 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2328 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2330 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2331 errmsg("\"%s\" is not a table", relname
)));
2334 * Most system catalogs can't be truncated at all, or at least not unless
2335 * allow_system_table_mods=on. As an exception, however, we allow
2336 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2337 * to change its relfilenode to match the old cluster, and allowing a
2338 * TRUNCATE command to be executed is the easiest way of doing that.
2340 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2341 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2343 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2344 errmsg("permission denied: \"%s\" is a system catalog",
2347 InvokeObjectTruncateHook(relid
);
2351 * Check that current user has the permission to truncate given relation.
2354 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2356 char *relname
= NameStr(reltuple
->relname
);
2357 AclResult aclresult
;
2359 /* Permissions checks */
2360 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2361 if (aclresult
!= ACLCHECK_OK
)
2362 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2367 * Set of extra sanity checks to check if a given relation is safe to
2368 * truncate. This is split with truncate_check_rel() as
2369 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2372 truncate_check_activity(Relation rel
)
2375 * Don't allow truncate on temp tables of other backends ... their local
2376 * buffer manager is not going to cope.
2378 if (RELATION_IS_OTHER_TEMP(rel
))
2380 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2381 errmsg("cannot truncate temporary tables of other sessions")));
2384 * Also check for active uses of the relation in the current transaction,
2385 * including open scans and pending AFTER trigger events.
2387 CheckTableNotInUse(rel
, "TRUNCATE");
2392 * returns the name corresponding to a typstorage/attstorage enum value
2395 storage_name(char c
)
2399 case TYPSTORAGE_PLAIN
:
2401 case TYPSTORAGE_EXTERNAL
:
2403 case TYPSTORAGE_EXTENDED
:
2405 case TYPSTORAGE_MAIN
:
2414 * Returns new schema given initial schema and superclasses.
2417 * 'columns' is the column/attribute definition for the table. (It's a list
2418 * of ColumnDef's.) It is destructively changed.
2419 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2420 * 'relpersistence' is the persistence type of the table.
2421 * 'is_partition' tells if the table is a partition.
2424 * 'supconstr' receives a list of constraints belonging to the parents,
2425 * updated as necessary to be valid for the child.
2426 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2427 * constraints coming from inheritance parents.
2430 * Completed schema list.
2433 * The order in which the attributes are inherited is very important.
2434 * Intuitively, the inherited attributes should come first. If a table
2435 * inherits from multiple parents, the order of those attributes are
2436 * according to the order of the parents specified in CREATE TABLE.
2438 * Here's an example:
2440 * create table person (name text, age int4, location point);
2441 * create table emp (salary int4, manager text) inherits(person);
2442 * create table student (gpa float8) inherits (person);
2443 * create table stud_emp (percent int4) inherits (emp, student);
2445 * The order of the attributes of stud_emp is:
2447 * person {1:name, 2:age, 3:location}
2449 * {6:gpa} student emp {4:salary, 5:manager}
2451 * stud_emp {7:percent}
2453 * If the same attribute name appears multiple times, then it appears
2454 * in the result table in the proper location for its first appearance.
2456 * Constraints (including not-null constraints) for the child table
2457 * are the union of all relevant constraints, from both the child schema
2458 * and parent tables. In addition, in legacy inheritance, each column that
2459 * appears in a primary key in any of the parents also gets a NOT NULL
2460 * constraint (partitioning doesn't need this, because the PK itself gets
2463 * The default value for a child column is defined as:
2464 * (1) If the child schema specifies a default, that value is used.
2465 * (2) If neither the child nor any parent specifies a default, then
2466 * the column will not have a default.
2467 * (3) If conflicting defaults are inherited from different parents
2468 * (and not overridden by the child), an error is raised.
2469 * (4) Otherwise the inherited default is used.
2471 * Note that the default-value infrastructure is used for generated
2472 * columns' expressions too, so most of the preceding paragraph applies
2473 * to generation expressions too. We insist that a child column be
2474 * generated if and only if its parent(s) are, but it need not have
2475 * the same generation expression.
2479 MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
2480 bool is_partition
, List
**supconstr
, List
**supnotnulls
)
2482 List
*inh_columns
= NIL
;
2483 List
*constraints
= NIL
;
2484 List
*nnconstraints
= NIL
;
2485 bool have_bogus_defaults
= false;
2487 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2488 List
*saved_columns
= NIL
;
2492 * Check for and reject tables with too many columns. We perform this
2493 * check relatively early for two reasons: (a) we don't run the risk of
2494 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2495 * okay if we're processing <= 1600 columns, but could take minutes to
2496 * execute if the user attempts to create a table with hundreds of
2497 * thousands of columns.
2499 * Note that we also need to check that we do not exceed this figure after
2500 * including columns from inherited relations.
2502 if (list_length(columns
) > MaxHeapAttributeNumber
)
2504 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2505 errmsg("tables can have at most %d columns",
2506 MaxHeapAttributeNumber
)));
2509 * Check for duplicate names in the explicit list of attributes.
2511 * Although we might consider merging such entries in the same way that we
2512 * handle name conflicts for inherited attributes, it seems to make more
2513 * sense to assume such conflicts are errors.
2515 * We don't use foreach() here because we have two nested loops over the
2516 * columns list, with possible element deletions in the inner one. If we
2517 * used foreach_delete_current() it could only fix up the state of one of
2518 * the loops, so it seems cleaner to use looping over list indexes for
2519 * both loops. Note that any deletion will happen beyond where the outer
2520 * loop is, so its index never needs adjustment.
2522 for (int coldefpos
= 0; coldefpos
< list_length(columns
); coldefpos
++)
2524 ColumnDef
*coldef
= list_nth_node(ColumnDef
, columns
, coldefpos
);
2526 if (!is_partition
&& coldef
->typeName
== NULL
)
2529 * Typed table column option that does not belong to a column from
2530 * the type. This works because the columns from the type come
2531 * first in the list. (We omit this check for partition column
2532 * lists; those are processed separately below.)
2535 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2536 errmsg("column \"%s\" does not exist",
2540 /* restpos scans all entries beyond coldef; incr is in loop body */
2541 for (int restpos
= coldefpos
+ 1; restpos
< list_length(columns
);)
2543 ColumnDef
*restdef
= list_nth_node(ColumnDef
, columns
, restpos
);
2545 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2547 if (coldef
->is_from_type
)
2550 * merge the column options into the column from the type
2552 coldef
->is_not_null
= restdef
->is_not_null
;
2553 coldef
->raw_default
= restdef
->raw_default
;
2554 coldef
->cooked_default
= restdef
->cooked_default
;
2555 coldef
->constraints
= restdef
->constraints
;
2556 coldef
->is_from_type
= false;
2557 columns
= list_delete_nth_cell(columns
, restpos
);
2561 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2562 errmsg("column \"%s\" specified more than once",
2571 * In case of a partition, there are no new column definitions, only dummy
2572 * ColumnDefs created for column constraints. Set them aside for now and
2573 * process them at the end.
2577 saved_columns
= columns
;
2582 * Scan the parents left-to-right, and merge their attributes to form a
2583 * list of inherited columns (inh_columns).
2588 Oid parent
= lfirst_oid(lc
);
2590 TupleDesc tupleDesc
;
2591 TupleConstr
*constr
;
2593 List
*inherited_defaults
;
2594 List
*cols_with_defaults
;
2599 Bitmapset
*nncols
= NULL
;
2601 /* caller already got lock */
2602 relation
= table_open(parent
, NoLock
);
2605 * Check for active uses of the parent partitioned table in the
2606 * current transaction, such as being used in some manner by an
2607 * enclosing command.
2610 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2613 * We do not allow partitioned tables and partitions to participate in
2614 * regular inheritance.
2616 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !is_partition
)
2618 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2619 errmsg("cannot inherit from partitioned table \"%s\"",
2620 RelationGetRelationName(relation
))));
2621 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2623 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2624 errmsg("cannot inherit from partition \"%s\"",
2625 RelationGetRelationName(relation
))));
2627 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2628 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2629 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2631 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2632 errmsg("inherited relation \"%s\" is not a table or foreign table",
2633 RelationGetRelationName(relation
))));
2636 * If the parent is permanent, so must be all of its partitions. Note
2637 * that inheritance allows that case.
2640 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2641 relpersistence
== RELPERSISTENCE_TEMP
)
2643 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2644 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2645 RelationGetRelationName(relation
))));
2647 /* Permanent rels cannot inherit from temporary ones */
2648 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2649 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2651 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2652 errmsg(!is_partition
2653 ? "cannot inherit from temporary relation \"%s\""
2654 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2655 RelationGetRelationName(relation
))));
2657 /* If existing rel is temp, it must belong to this session */
2658 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2659 !relation
->rd_islocaltemp
)
2661 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2662 errmsg(!is_partition
2663 ? "cannot inherit from temporary relation of another session"
2664 : "cannot create as partition of temporary relation of another session")));
2667 * We should have an UNDER permission flag for this, but for now,
2668 * demand that creator of a child table own the parent.
2670 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2671 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2672 RelationGetRelationName(relation
));
2674 tupleDesc
= RelationGetDescr(relation
);
2675 constr
= tupleDesc
->constr
;
2678 * newattmap->attnums[] will contain the child-table attribute numbers
2679 * for the attributes of this parent table. (They are not the same
2680 * for parents after the first one, nor if we have dropped columns.)
2682 newattmap
= make_attrmap(tupleDesc
->natts
);
2684 /* We can't process inherited defaults until newattmap is complete. */
2685 inherited_defaults
= cols_with_defaults
= NIL
;
2688 * All columns that are part of the parent's primary key need to be
2689 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2690 * constraint (if they don't have one already). Also, we request
2691 * attnotnull on columns that have a not-null constraint that's not
2692 * marked NO INHERIT.
2694 pkattrs
= RelationGetIndexAttrBitmap(relation
,
2695 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
2696 nnconstrs
= RelationGetNotNullConstraints(RelationGetRelid(relation
), true);
2697 foreach(lc1
, nnconstrs
)
2698 nncols
= bms_add_member(nncols
,
2699 ((CookedConstraint
*) lfirst(lc1
))->attnum
);
2701 for (AttrNumber parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2704 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2706 char *attributeName
= NameStr(attribute
->attname
);
2709 ColumnDef
*mergeddef
;
2712 * Ignore dropped columns in the parent.
2714 if (attribute
->attisdropped
)
2715 continue; /* leave newattmap->attnums entry as zero */
2718 * Create new column definition
2720 newdef
= makeColumnDef(attributeName
, attribute
->atttypid
,
2721 attribute
->atttypmod
, attribute
->attcollation
);
2722 newdef
->storage
= attribute
->attstorage
;
2723 newdef
->generated
= attribute
->attgenerated
;
2724 if (CompressionMethodIsValid(attribute
->attcompression
))
2725 newdef
->compression
=
2726 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2729 * Regular inheritance children are independent enough not to
2730 * inherit identity columns. But partitions are integral part of
2731 * a partitioned table and inherit identity column.
2734 newdef
->identity
= attribute
->attidentity
;
2737 * Does it match some previously considered column from another
2740 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2741 if (exist_attno
> 0)
2744 * Yes, try to merge the two column definitions.
2746 mergeddef
= MergeInheritedAttribute(inh_columns
, exist_attno
, newdef
);
2748 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2751 * Partitions have only one parent, so conflict should never
2754 Assert(!is_partition
);
2759 * No, create a new inherited column
2761 newdef
->inhcount
= 1;
2762 newdef
->is_local
= false;
2763 inh_columns
= lappend(inh_columns
, newdef
);
2765 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2771 * mark attnotnull if parent has it and it's not NO INHERIT
2773 if (bms_is_member(parent_attno
, nncols
) ||
2774 bms_is_member(parent_attno
- FirstLowInvalidHeapAttributeNumber
,
2776 mergeddef
->is_not_null
= true;
2779 * In regular inheritance, columns in the parent's primary key get
2780 * an extra not-null constraint. Partitioning doesn't need this,
2781 * because the PK itself is going to be cloned to the partition.
2783 if (!is_partition
&&
2784 bms_is_member(parent_attno
-
2785 FirstLowInvalidHeapAttributeNumber
,
2788 CookedConstraint
*nn
;
2790 nn
= palloc(sizeof(CookedConstraint
));
2791 nn
->contype
= CONSTR_NOTNULL
;
2792 nn
->conoid
= InvalidOid
;
2794 nn
->attnum
= newattmap
->attnums
[parent_attno
- 1];
2796 nn
->skip_validation
= false;
2797 nn
->is_local
= false;
2799 nn
->is_no_inherit
= false;
2801 nnconstraints
= lappend(nnconstraints
, nn
);
2805 * Locate default/generation expression if any
2807 if (attribute
->atthasdef
)
2811 this_default
= TupleDescGetDefault(tupleDesc
, parent_attno
);
2812 if (this_default
== NULL
)
2813 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2814 parent_attno
, RelationGetRelationName(relation
));
2817 * If it's a GENERATED default, it might contain Vars that
2818 * need to be mapped to the inherited column(s)' new numbers.
2819 * We can't do that till newattmap is ready, so just remember
2820 * all the inherited default expressions for the moment.
2822 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2823 cols_with_defaults
= lappend(cols_with_defaults
, mergeddef
);
2828 * Now process any inherited default expressions, adjusting attnos
2829 * using the completed newattmap map.
2831 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2833 Node
*this_default
= (Node
*) lfirst(lc1
);
2834 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2835 bool found_whole_row
;
2837 /* Adjust Vars to match new table's column numbering */
2838 this_default
= map_variable_attnos(this_default
,
2841 InvalidOid
, &found_whole_row
);
2844 * For the moment we have to reject whole-row variables. We could
2845 * convert them, if we knew the new table's rowtype OID, but that
2846 * hasn't been assigned yet. (A variable could only appear in a
2847 * generation expression, so the error message is correct.)
2849 if (found_whole_row
)
2851 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2852 errmsg("cannot convert whole-row table reference"),
2853 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2855 RelationGetRelationName(relation
))));
2858 * If we already had a default from some prior parent, check to
2859 * see if they are the same. If so, no problem; if not, mark the
2860 * column as having a bogus default. Below, we will complain if
2861 * the bogus default isn't overridden by the child columns.
2863 Assert(def
->raw_default
== NULL
);
2864 if (def
->cooked_default
== NULL
)
2865 def
->cooked_default
= this_default
;
2866 else if (!equal(def
->cooked_default
, this_default
))
2868 def
->cooked_default
= &bogus_marker
;
2869 have_bogus_defaults
= true;
2874 * Now copy the CHECK constraints of this parent, adjusting attnos
2875 * using the completed newattmap map. Identically named constraints
2876 * are merged if possible, else we throw error.
2878 if (constr
&& constr
->num_check
> 0)
2880 ConstrCheck
*check
= constr
->check
;
2882 for (int i
= 0; i
< constr
->num_check
; i
++)
2884 char *name
= check
[i
].ccname
;
2886 bool found_whole_row
;
2888 /* ignore if the constraint is non-inheritable */
2889 if (check
[i
].ccnoinherit
)
2892 /* Adjust Vars to match new table's column numbering */
2893 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2896 InvalidOid
, &found_whole_row
);
2899 * For the moment we have to reject whole-row variables. We
2900 * could convert them, if we knew the new table's rowtype OID,
2901 * but that hasn't been assigned yet.
2903 if (found_whole_row
)
2905 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2906 errmsg("cannot convert whole-row table reference"),
2907 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2909 RelationGetRelationName(relation
))));
2911 constraints
= MergeCheckConstraint(constraints
, name
, expr
);
2916 * Also copy the not-null constraints from this parent. The
2917 * attnotnull markings were already installed above.
2919 foreach(lc1
, nnconstrs
)
2921 CookedConstraint
*nn
= lfirst(lc1
);
2923 Assert(nn
->contype
== CONSTR_NOTNULL
);
2925 nn
->attnum
= newattmap
->attnums
[nn
->attnum
- 1];
2926 nn
->is_local
= false;
2929 nnconstraints
= lappend(nnconstraints
, nn
);
2932 free_attrmap(newattmap
);
2935 * Close the parent rel, but keep our lock on it until xact commit.
2936 * That will prevent someone else from deleting or ALTERing the parent
2937 * before the child is committed.
2939 table_close(relation
, NoLock
);
2943 * If we had no inherited attributes, the result columns are just the
2944 * explicitly declared columns. Otherwise, we need to merge the declared
2945 * columns into the inherited column list. Although, we never have any
2946 * explicitly declared columns if the table is a partition.
2948 if (inh_columns
!= NIL
)
2950 int newcol_attno
= 0;
2952 foreach(lc
, columns
)
2954 ColumnDef
*newdef
= lfirst_node(ColumnDef
, lc
);
2955 char *attributeName
= newdef
->colname
;
2959 * Partitions have only one parent and have no column definitions
2960 * of their own, so conflict should never occur.
2962 Assert(!is_partition
);
2967 * Does it match some inherited column?
2969 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2970 if (exist_attno
> 0)
2973 * Yes, try to merge the two column definitions.
2975 MergeChildAttribute(inh_columns
, exist_attno
, newcol_attno
, newdef
);
2980 * No, attach new column unchanged to result columns.
2982 inh_columns
= lappend(inh_columns
, newdef
);
2986 columns
= inh_columns
;
2989 * Check that we haven't exceeded the legal # of columns after merging
2990 * in inherited columns.
2992 if (list_length(columns
) > MaxHeapAttributeNumber
)
2994 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2995 errmsg("tables can have at most %d columns",
2996 MaxHeapAttributeNumber
)));
3000 * Now that we have the column definition list for a partition, we can
3001 * check whether the columns referenced in the column constraint specs
3002 * actually exist. Also, merge column defaults.
3006 foreach(lc
, saved_columns
)
3008 ColumnDef
*restdef
= lfirst(lc
);
3014 ColumnDef
*coldef
= lfirst(l
);
3016 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
3021 * Check for conflicts related to generated columns.
3023 * Same rules as above: generated-ness has to match the
3024 * parent, but the contents of the generation expression
3027 if (coldef
->generated
)
3029 if (restdef
->raw_default
&& !restdef
->generated
)
3031 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3032 errmsg("column \"%s\" inherits from generated column but specifies default",
3033 restdef
->colname
)));
3034 if (restdef
->identity
)
3036 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3037 errmsg("column \"%s\" inherits from generated column but specifies identity",
3038 restdef
->colname
)));
3042 if (restdef
->generated
)
3044 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3045 errmsg("child column \"%s\" specifies generation expression",
3047 errhint("A child table column cannot be generated unless its parent column is.")));
3051 * Override the parent's default value for this column
3052 * (coldef->cooked_default) with the partition's local
3053 * definition (restdef->raw_default), if there's one. It
3054 * should be physically impossible to get a cooked default
3055 * in the local definition or a raw default in the
3056 * inherited definition, but make sure they're nulls, for
3059 Assert(restdef
->cooked_default
== NULL
);
3060 Assert(coldef
->raw_default
== NULL
);
3061 if (restdef
->raw_default
)
3063 coldef
->raw_default
= restdef
->raw_default
;
3064 coldef
->cooked_default
= NULL
;
3069 /* complain for constraints on columns not in parent */
3072 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3073 errmsg("column \"%s\" does not exist",
3074 restdef
->colname
)));
3079 * If we found any conflicting parent default values, check to make sure
3080 * they were overridden by the child.
3082 if (have_bogus_defaults
)
3084 foreach(lc
, columns
)
3086 ColumnDef
*def
= lfirst(lc
);
3088 if (def
->cooked_default
== &bogus_marker
)
3092 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3093 errmsg("column \"%s\" inherits conflicting generation expressions",
3095 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3098 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3099 errmsg("column \"%s\" inherits conflicting default values",
3101 errhint("To resolve the conflict, specify a default explicitly.")));
3106 *supconstr
= constraints
;
3107 *supnotnulls
= nnconstraints
;
3114 * MergeCheckConstraint
3115 * Try to merge an inherited CHECK constraint with previous ones
3117 * If we inherit identically-named constraints from multiple parents, we must
3118 * merge them, or throw an error if they don't have identical definitions.
3120 * constraints is a list of CookedConstraint structs for previous constraints.
3122 * If the new constraint matches an existing one, then the existing
3123 * constraint's inheritance count is updated. If there is a conflict (same
3124 * name but different expression), throw an error. If the constraint neither
3125 * matches nor conflicts with an existing one, a new constraint is appended to
3129 MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
)
3132 CookedConstraint
*newcon
;
3134 foreach(lc
, constraints
)
3136 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3138 Assert(ccon
->contype
== CONSTR_CHECK
);
3140 /* Non-matching names never conflict */
3141 if (strcmp(ccon
->name
, name
) != 0)
3144 if (equal(expr
, ccon
->expr
))
3146 /* OK to merge constraint with existing */
3148 if (ccon
->inhcount
< 0)
3150 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3151 errmsg("too many inheritance parents"));
3156 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3157 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3162 * Constraint couldn't be merged with an existing one and also didn't
3163 * conflict with an existing one, so add it as a new one to the list.
3165 newcon
= palloc0_object(CookedConstraint
);
3166 newcon
->contype
= CONSTR_CHECK
;
3167 newcon
->name
= pstrdup(name
);
3168 newcon
->expr
= expr
;
3169 newcon
->inhcount
= 1;
3170 return lappend(constraints
, newcon
);
3174 * MergeChildAttribute
3175 * Merge given child attribute definition into given inherited attribute.
3178 * 'inh_columns' is the list of inherited ColumnDefs.
3179 * 'exist_attno' is the number of the inherited attribute in inh_columns
3180 * 'newcol_attno' is the attribute number in child table's schema definition
3181 * 'newdef' is the column/attribute definition from the child table.
3183 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3184 * ColumnDef remains unchanged.
3187 * - The attribute is merged according to the rules laid out in the prologue
3188 * of MergeAttributes().
3189 * - If matching inherited attribute exists but the child attribute can not be
3190 * merged into it, the function throws respective errors.
3191 * - A partition can not have its own column definitions. Hence this function
3192 * is applicable only to a regular inheritance child.
3195 MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
)
3197 char *attributeName
= newdef
->colname
;
3206 if (exist_attno
== newcol_attno
)
3208 (errmsg("merging column \"%s\" with inherited definition",
3212 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
3213 errdetail("User-specified column moved to the position of the inherited column.")));
3215 inhdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3218 * Must have the same type and typmod
3220 typenameTypeIdAndMod(NULL
, inhdef
->typeName
, &inhtypeid
, &inhtypmod
);
3221 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3222 if (inhtypeid
!= newtypeid
|| inhtypmod
!= newtypmod
)
3224 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3225 errmsg("column \"%s\" has a type conflict",
3227 errdetail("%s versus %s",
3228 format_type_with_typemod(inhtypeid
, inhtypmod
),
3229 format_type_with_typemod(newtypeid
, newtypmod
))));
3232 * Must have the same collation
3234 inhcollid
= GetColumnDefCollation(NULL
, inhdef
, inhtypeid
);
3235 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3236 if (inhcollid
!= newcollid
)
3238 (errcode(ERRCODE_COLLATION_MISMATCH
),
3239 errmsg("column \"%s\" has a collation conflict",
3241 errdetail("\"%s\" versus \"%s\"",
3242 get_collation_name(inhcollid
),
3243 get_collation_name(newcollid
))));
3246 * Identity is never inherited by a regular inheritance child. Pick
3247 * child's identity definition if there's one.
3249 inhdef
->identity
= newdef
->identity
;
3252 * Copy storage parameter
3254 if (inhdef
->storage
== 0)
3255 inhdef
->storage
= newdef
->storage
;
3256 else if (newdef
->storage
!= 0 && inhdef
->storage
!= newdef
->storage
)
3258 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3259 errmsg("column \"%s\" has a storage parameter conflict",
3261 errdetail("%s versus %s",
3262 storage_name(inhdef
->storage
),
3263 storage_name(newdef
->storage
))));
3266 * Copy compression parameter
3268 if (inhdef
->compression
== NULL
)
3269 inhdef
->compression
= newdef
->compression
;
3270 else if (newdef
->compression
!= NULL
)
3272 if (strcmp(inhdef
->compression
, newdef
->compression
) != 0)
3274 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3275 errmsg("column \"%s\" has a compression method conflict",
3277 errdetail("%s versus %s", inhdef
->compression
, newdef
->compression
)));
3281 * Merge of not-null constraints = OR 'em together
3283 inhdef
->is_not_null
|= newdef
->is_not_null
;
3286 * Check for conflicts related to generated columns.
3288 * If the parent column is generated, the child column will be made a
3289 * generated column if it isn't already. If it is a generated column,
3290 * we'll take its generation expression in preference to the parent's. We
3291 * must check that the child column doesn't specify a default value or
3292 * identity, which matches the rules for a single column in
3295 * Conversely, if the parent column is not generated, the child column
3296 * can't be either. (We used to allow that, but it results in being able
3297 * to override the generation expression via UPDATEs through the parent.)
3299 if (inhdef
->generated
)
3301 if (newdef
->raw_default
&& !newdef
->generated
)
3303 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3304 errmsg("column \"%s\" inherits from generated column but specifies default",
3306 if (newdef
->identity
)
3308 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3309 errmsg("column \"%s\" inherits from generated column but specifies identity",
3314 if (newdef
->generated
)
3316 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3317 errmsg("child column \"%s\" specifies generation expression",
3319 errhint("A child table column cannot be generated unless its parent column is.")));
3323 * If new def has a default, override previous default
3325 if (newdef
->raw_default
!= NULL
)
3327 inhdef
->raw_default
= newdef
->raw_default
;
3328 inhdef
->cooked_default
= newdef
->cooked_default
;
3331 /* Mark the column as locally defined */
3332 inhdef
->is_local
= true;
3336 * MergeInheritedAttribute
3337 * Merge given parent attribute definition into specified attribute
3338 * inherited from the previous parents.
3341 * 'inh_columns' is the list of previously inherited ColumnDefs.
3342 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3343 * 'newdef' is the new parent column/attribute definition to be merged.
3345 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3348 * - The attribute is merged according to the rules laid out in the prologue
3349 * of MergeAttributes().
3350 * - If matching inherited attribute exists but the new attribute can not be
3351 * merged into it, the function throws respective errors.
3352 * - A partition inherits from only a single parent. Hence this function is
3353 * applicable only to a regular inheritance.
3356 MergeInheritedAttribute(List
*inh_columns
,
3358 const ColumnDef
*newdef
)
3360 char *attributeName
= newdef
->colname
;
3370 (errmsg("merging multiple inherited definitions of column \"%s\"",
3372 prevdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3375 * Must have the same type and typmod
3377 typenameTypeIdAndMod(NULL
, prevdef
->typeName
, &prevtypeid
, &prevtypmod
);
3378 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3379 if (prevtypeid
!= newtypeid
|| prevtypmod
!= newtypmod
)
3381 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3382 errmsg("inherited column \"%s\" has a type conflict",
3384 errdetail("%s versus %s",
3385 format_type_with_typemod(prevtypeid
, prevtypmod
),
3386 format_type_with_typemod(newtypeid
, newtypmod
))));
3389 * Must have the same collation
3391 prevcollid
= GetColumnDefCollation(NULL
, prevdef
, prevtypeid
);
3392 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3393 if (prevcollid
!= newcollid
)
3395 (errcode(ERRCODE_COLLATION_MISMATCH
),
3396 errmsg("inherited column \"%s\" has a collation conflict",
3398 errdetail("\"%s\" versus \"%s\"",
3399 get_collation_name(prevcollid
),
3400 get_collation_name(newcollid
))));
3403 * Copy/check storage parameter
3405 if (prevdef
->storage
== 0)
3406 prevdef
->storage
= newdef
->storage
;
3407 else if (prevdef
->storage
!= newdef
->storage
)
3409 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3410 errmsg("inherited column \"%s\" has a storage parameter conflict",
3412 errdetail("%s versus %s",
3413 storage_name(prevdef
->storage
),
3414 storage_name(newdef
->storage
))));
3417 * Copy/check compression parameter
3419 if (prevdef
->compression
== NULL
)
3420 prevdef
->compression
= newdef
->compression
;
3421 else if (strcmp(prevdef
->compression
, newdef
->compression
) != 0)
3423 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3424 errmsg("column \"%s\" has a compression method conflict",
3426 errdetail("%s versus %s", prevdef
->compression
, newdef
->compression
)));
3429 * Check for GENERATED conflicts
3431 if (prevdef
->generated
!= newdef
->generated
)
3433 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3434 errmsg("inherited column \"%s\" has a generation conflict",
3438 * Default and other constraints are handled by the caller.
3441 prevdef
->inhcount
++;
3442 if (prevdef
->inhcount
< 0)
3444 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3445 errmsg("too many inheritance parents"));
3451 * StoreCatalogInheritance
3452 * Updates the system catalogs with proper inheritance information.
3454 * supers is a list of the OIDs of the new relation's direct ancestors.
3457 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3458 bool child_is_partition
)
3467 Assert(OidIsValid(relationId
));
3473 * Store INHERITS information in pg_inherits using direct ancestors only.
3474 * Also enter dependencies on the direct ancestors, and make sure they are
3475 * marked with relhassubclass = true.
3477 * (Once upon a time, both direct and indirect ancestors were found here
3478 * and then entered into pg_ipl. Since that catalog doesn't exist
3479 * anymore, there's no need to look for indirect ancestors.)
3481 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3484 foreach(entry
, supers
)
3486 Oid parentOid
= lfirst_oid(entry
);
3488 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3489 child_is_partition
);
3493 table_close(relation
, RowExclusiveLock
);
3497 * Make catalog entries showing relationId as being an inheritance child
3498 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3501 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3502 int32 seqNumber
, Relation inhRelation
,
3503 bool child_is_partition
)
3505 ObjectAddress childobject
,
3508 /* store the pg_inherits row */
3509 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3512 * Store a dependency too
3514 parentobject
.classId
= RelationRelationId
;
3515 parentobject
.objectId
= parentOid
;
3516 parentobject
.objectSubId
= 0;
3517 childobject
.classId
= RelationRelationId
;
3518 childobject
.objectId
= relationId
;
3519 childobject
.objectSubId
= 0;
3521 recordDependencyOn(&childobject
, &parentobject
,
3522 child_dependency_type(child_is_partition
));
3525 * Post creation hook of this inheritance. Since object_access_hook
3526 * doesn't take multiple object identifiers, we relay oid of parent
3527 * relation using auxiliary_id argument.
3529 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3534 * Mark the parent as having subclasses.
3536 SetRelationHasSubclass(parentOid
, true);
3540 * Look for an existing column entry with the given name.
3542 * Returns the index (starting with 1) if attribute already exists in columns,
3546 findAttrByName(const char *attributeName
, const List
*columns
)
3551 foreach(lc
, columns
)
3553 if (strcmp(attributeName
, lfirst_node(ColumnDef
, lc
)->colname
) == 0)
3563 * SetRelationHasSubclass
3564 * Set the value of the relation's relhassubclass field in pg_class.
3566 * NOTE: caller must be holding an appropriate lock on the relation.
3567 * ShareUpdateExclusiveLock is sufficient.
3569 * NOTE: an important side-effect of this operation is that an SI invalidation
3570 * message is sent out to all backends --- including me --- causing plans
3571 * referencing the relation to be rebuilt with the new list of children.
3572 * This must happen even if we find that no change is needed in the pg_class
3576 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3578 Relation relationRelation
;
3580 Form_pg_class classtuple
;
3583 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3585 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3586 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3587 if (!HeapTupleIsValid(tuple
))
3588 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3589 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3591 if (classtuple
->relhassubclass
!= relhassubclass
)
3593 classtuple
->relhassubclass
= relhassubclass
;
3594 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3598 /* no need to change tuple, but force relcache rebuild anyway */
3599 CacheInvalidateRelcacheByTuple(tuple
);
3602 heap_freetuple(tuple
);
3603 table_close(relationRelation
, RowExclusiveLock
);
3607 * CheckRelationTableSpaceMove
3608 * Check if relation can be moved to new tablespace.
3610 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3612 * Returns true if the relation can be moved to the new tablespace; raises
3613 * an error if it is not possible to do the move; returns false if the move
3614 * would have no effect.
3617 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3619 Oid oldTableSpaceId
;
3622 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3625 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3626 if (newTableSpaceId
== oldTableSpaceId
||
3627 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3631 * We cannot support moving mapped relations into different tablespaces.
3632 * (In particular this eliminates all shared catalogs.)
3634 if (RelationIsMapped(rel
))
3636 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3637 errmsg("cannot move system relation \"%s\"",
3638 RelationGetRelationName(rel
))));
3640 /* Cannot move a non-shared relation into pg_global */
3641 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3643 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3644 errmsg("only shared relations can be placed in pg_global tablespace")));
3647 * Do not allow moving temp tables of other backends ... their local
3648 * buffer manager is not going to cope.
3650 if (RELATION_IS_OTHER_TEMP(rel
))
3652 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3653 errmsg("cannot move temporary tables of other sessions")));
3659 * SetRelationTableSpace
3660 * Set new reltablespace and relfilenumber in pg_class entry.
3662 * newTableSpaceId is the new tablespace for the relation, and
3663 * newRelFilenumber its new filenumber. If newRelFilenumber is
3664 * InvalidRelFileNumber, this field is not updated.
3666 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3668 * The caller of this routine had better check if a relation can be
3669 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3670 * first, and is responsible for making the change visible with
3671 * CommandCounterIncrement().
3674 SetRelationTableSpace(Relation rel
,
3675 Oid newTableSpaceId
,
3676 RelFileNumber newRelFilenumber
)
3680 Form_pg_class rd_rel
;
3681 Oid reloid
= RelationGetRelid(rel
);
3683 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3685 /* Get a modifiable copy of the relation's pg_class row. */
3686 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3688 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3689 if (!HeapTupleIsValid(tuple
))
3690 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3691 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3693 /* Update the pg_class row. */
3694 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3695 InvalidOid
: newTableSpaceId
;
3696 if (RelFileNumberIsValid(newRelFilenumber
))
3697 rd_rel
->relfilenode
= newRelFilenumber
;
3698 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
3701 * Record dependency on tablespace. This is only required for relations
3702 * that have no physical storage.
3704 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3705 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3706 rd_rel
->reltablespace
);
3708 heap_freetuple(tuple
);
3709 table_close(pg_class
, RowExclusiveLock
);
3713 * renameatt_check - basic sanity checks before attribute rename
3716 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3718 char relkind
= classform
->relkind
;
3720 if (classform
->reloftype
&& !recursing
)
3722 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3723 errmsg("cannot rename column of typed table")));
3726 * Renaming the columns of sequences or toast tables doesn't actually
3727 * break anything from the system's point of view, since internal
3728 * references are by attnum. But it doesn't seem right to allow users to
3729 * change names that are hardcoded into the system, hence the following
3732 if (relkind
!= RELKIND_RELATION
&&
3733 relkind
!= RELKIND_VIEW
&&
3734 relkind
!= RELKIND_MATVIEW
&&
3735 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3736 relkind
!= RELKIND_INDEX
&&
3737 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3738 relkind
!= RELKIND_FOREIGN_TABLE
&&
3739 relkind
!= RELKIND_PARTITIONED_TABLE
)
3741 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3742 errmsg("cannot rename columns of relation \"%s\"",
3743 NameStr(classform
->relname
)),
3744 errdetail_relkind_not_supported(relkind
)));
3747 * permissions checking. only the owner of a class can change its schema.
3749 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3750 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3751 NameStr(classform
->relname
));
3752 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3754 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3755 errmsg("permission denied: \"%s\" is a system catalog",
3756 NameStr(classform
->relname
))));
3760 * renameatt_internal - workhorse for renameatt
3762 * Return value is the attribute number in the 'myrelid' relation.
3765 renameatt_internal(Oid myrelid
,
3766 const char *oldattname
,
3767 const char *newattname
,
3770 int expected_parents
,
3771 DropBehavior behavior
)
3773 Relation targetrelation
;
3774 Relation attrelation
;
3776 Form_pg_attribute attform
;
3780 * Grab an exclusive lock on the target table, which we will NOT release
3781 * until end of transaction.
3783 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3784 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3787 * if the 'recurse' flag is set then we are supposed to rename this
3788 * attribute in all classes that inherit from 'relname' (as well as in
3791 * any permissions or problems with duplicate attributes will cause the
3792 * whole transaction to abort, which is what we want -- all or nothing.
3802 * we need the number of parents for each child so that the recursive
3803 * calls to renameatt() can determine whether there are any parents
3804 * outside the inheritance hierarchy being processed.
3806 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3810 * find_all_inheritors does the recursive search of the inheritance
3811 * hierarchy, so all we have to do is process all of the relids in the
3812 * list that it returns.
3814 forboth(lo
, child_oids
, li
, child_numparents
)
3816 Oid childrelid
= lfirst_oid(lo
);
3817 int numparents
= lfirst_int(li
);
3819 if (childrelid
== myrelid
)
3821 /* note we need not recurse again */
3822 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3828 * If we are told not to recurse, there had better not be any child
3829 * tables; else the rename would put them out of step.
3831 * expected_parents will only be 0 if we are not already recursing.
3833 if (expected_parents
== 0 &&
3834 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3836 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3837 errmsg("inherited column \"%s\" must be renamed in child tables too",
3841 /* rename attributes in typed tables of composite type */
3842 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3847 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3848 RelationGetRelationName(targetrelation
),
3851 foreach(lo
, child_oids
)
3852 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3855 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3857 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3858 if (!HeapTupleIsValid(atttup
))
3860 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3861 errmsg("column \"%s\" does not exist",
3863 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3865 attnum
= attform
->attnum
;
3868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3869 errmsg("cannot rename system column \"%s\"",
3873 * if the attribute is inherited, forbid the renaming. if this is a
3874 * top-level call to renameatt(), then expected_parents will be 0, so the
3875 * effect of this code will be to prohibit the renaming if the attribute
3876 * is inherited at all. if this is a recursive call to renameatt(),
3877 * expected_parents will be the number of parents the current relation has
3878 * within the inheritance hierarchy being processed, so we'll prohibit the
3879 * renaming only if there are additional parents from elsewhere.
3881 if (attform
->attinhcount
> expected_parents
)
3883 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3884 errmsg("cannot rename inherited column \"%s\"",
3887 /* new name should not already exist */
3888 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3890 /* apply the update */
3891 namestrcpy(&(attform
->attname
), newattname
);
3893 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3895 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3897 heap_freetuple(atttup
);
3899 table_close(attrelation
, RowExclusiveLock
);
3901 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3907 * Perform permissions and integrity checks before acquiring a relation lock.
3910 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3916 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3917 if (!HeapTupleIsValid(tuple
))
3918 return; /* concurrently dropped */
3919 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3920 renameatt_check(relid
, form
, false);
3921 ReleaseSysCache(tuple
);
3925 * renameatt - changes the name of an attribute in a relation
3927 * The returned ObjectAddress is that of the renamed column.
3930 renameatt(RenameStmt
*stmt
)
3934 ObjectAddress address
;
3936 /* lock level taken here should match renameatt_internal */
3937 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3938 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3939 RangeVarCallbackForRenameAttribute
,
3942 if (!OidIsValid(relid
))
3945 (errmsg("relation \"%s\" does not exist, skipping",
3946 stmt
->relation
->relname
)));
3947 return InvalidObjectAddress
;
3951 renameatt_internal(relid
,
3952 stmt
->subname
, /* old att name */
3953 stmt
->newname
, /* new att name */
3954 stmt
->relation
->inh
, /* recursive? */
3955 false, /* recursing? */
3956 0, /* expected inhcount */
3959 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3965 * same logic as renameatt_internal
3967 static ObjectAddress
3968 rename_constraint_internal(Oid myrelid
,
3970 const char *oldconname
,
3971 const char *newconname
,
3974 int expected_parents
)
3976 Relation targetrelation
= NULL
;
3979 Form_pg_constraint con
;
3980 ObjectAddress address
;
3982 Assert(!myrelid
|| !mytypid
);
3986 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
3990 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3993 * don't tell it whether we're recursing; we allow changing typed
3996 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
3998 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
4001 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
4002 if (!HeapTupleIsValid(tuple
))
4003 elog(ERROR
, "cache lookup failed for constraint %u",
4005 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
4008 (con
->contype
== CONSTRAINT_CHECK
||
4009 con
->contype
== CONSTRAINT_NOTNULL
) &&
4019 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
4022 forboth(lo
, child_oids
, li
, child_numparents
)
4024 Oid childrelid
= lfirst_oid(lo
);
4025 int numparents
= lfirst_int(li
);
4027 if (childrelid
== myrelid
)
4030 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
4035 if (expected_parents
== 0 &&
4036 find_inheritance_children(myrelid
, NoLock
) != NIL
)
4038 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4039 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4043 if (con
->coninhcount
> expected_parents
)
4045 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
4046 errmsg("cannot rename inherited constraint \"%s\"",
4051 && (con
->contype
== CONSTRAINT_PRIMARY
4052 || con
->contype
== CONSTRAINT_UNIQUE
4053 || con
->contype
== CONSTRAINT_EXCLUSION
))
4054 /* rename the index; this renames the constraint as well */
4055 RenameRelationInternal(con
->conindid
, newconname
, false, true);
4057 RenameConstraintById(constraintOid
, newconname
);
4059 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
4061 ReleaseSysCache(tuple
);
4066 * Invalidate relcache so as others can see the new constraint name.
4068 CacheInvalidateRelcache(targetrelation
);
4070 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
4077 RenameConstraint(RenameStmt
*stmt
)
4079 Oid relid
= InvalidOid
;
4080 Oid typid
= InvalidOid
;
4082 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
4087 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
4088 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
4089 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
4090 if (!HeapTupleIsValid(tup
))
4091 elog(ERROR
, "cache lookup failed for type %u", typid
);
4092 checkDomainOwner(tup
);
4093 ReleaseSysCache(tup
);
4094 table_close(rel
, NoLock
);
4098 /* lock level taken here should match rename_constraint_internal */
4099 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
4100 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4101 RangeVarCallbackForRenameAttribute
,
4103 if (!OidIsValid(relid
))
4106 (errmsg("relation \"%s\" does not exist, skipping",
4107 stmt
->relation
->relname
)));
4108 return InvalidObjectAddress
;
4113 rename_constraint_internal(relid
, typid
,
4117 stmt
->relation
->inh
), /* recursive? */
4118 false, /* recursing? */
4119 0 /* expected inhcount */ );
4123 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4127 RenameRelation(RenameStmt
*stmt
)
4129 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
4131 ObjectAddress address
;
4134 * Grab an exclusive lock on the target table, index, sequence, view,
4135 * materialized view, or foreign table, which we will NOT release until
4136 * end of transaction.
4138 * Lock level used here should match RenameRelationInternal, to avoid lock
4139 * escalation. However, because ALTER INDEX can be used with any relation
4140 * type, we mustn't believe without verification.
4148 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
4150 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4151 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4152 RangeVarCallbackForAlterRelation
,
4155 if (!OidIsValid(relid
))
4158 (errmsg("relation \"%s\" does not exist, skipping",
4159 stmt
->relation
->relname
)));
4160 return InvalidObjectAddress
;
4164 * We allow mismatched statement and object types (e.g., ALTER INDEX
4165 * to rename a table), but we might've used the wrong lock level. If
4166 * that happens, retry with the correct lock level. We don't bother
4167 * if we already acquired AccessExclusiveLock with an index, however.
4169 relkind
= get_rel_relkind(relid
);
4170 obj_is_index
= (relkind
== RELKIND_INDEX
||
4171 relkind
== RELKIND_PARTITIONED_INDEX
);
4172 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
4175 UnlockRelationOid(relid
, lockmode
);
4176 is_index_stmt
= obj_is_index
;
4180 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
4182 ObjectAddressSet(address
, RelationRelationId
, relid
);
4188 * RenameRelationInternal - change the name of a relation
4191 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
4193 Relation targetrelation
;
4194 Relation relrelation
; /* for RELATION relation */
4196 Form_pg_class relform
;
4200 * Grab a lock on the target relation, which we will NOT release until end
4201 * of transaction. We need at least a self-exclusive lock so that
4202 * concurrent DDL doesn't overwrite the rename if they start updating
4203 * while still seeing the old version. The lock also guards against
4204 * triggering relcache reloads in concurrent sessions, which might not
4205 * handle this information changing under them. For indexes, we can use a
4206 * reduced lock level because RelationReloadIndexInfo() handles indexes
4209 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
4210 namespaceId
= RelationGetNamespace(targetrelation
);
4213 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4215 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4217 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4218 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4219 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4220 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4222 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
4224 (errcode(ERRCODE_DUPLICATE_TABLE
),
4225 errmsg("relation \"%s\" already exists",
4229 * RenameRelation is careful not to believe the caller's idea of the
4230 * relation kind being handled. We don't have to worry about this, but
4231 * let's not be totally oblivious to it. We can process an index as
4232 * not-an-index, but not the other way around.
4235 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4236 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4239 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4240 * because it's a copy...)
4242 namestrcpy(&(relform
->relname
), newrelname
);
4244 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4246 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4247 InvalidOid
, is_internal
);
4249 heap_freetuple(reltup
);
4250 table_close(relrelation
, RowExclusiveLock
);
4253 * Also rename the associated type, if any.
4255 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4256 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4257 newrelname
, namespaceId
);
4260 * Also rename the associated constraint, if any.
4262 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4263 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4265 Oid constraintId
= get_index_constraint(myrelid
);
4267 if (OidIsValid(constraintId
))
4268 RenameConstraintById(constraintId
, newrelname
);
4272 * Close rel, but keep lock!
4274 relation_close(targetrelation
, NoLock
);
4278 * ResetRelRewrite - reset relrewrite
4281 ResetRelRewrite(Oid myrelid
)
4283 Relation relrelation
; /* for RELATION relation */
4285 Form_pg_class relform
;
4288 * Find relation's pg_class tuple.
4290 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4292 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4293 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4294 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4295 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4298 * Update pg_class tuple.
4300 relform
->relrewrite
= InvalidOid
;
4302 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4304 heap_freetuple(reltup
);
4305 table_close(relrelation
, RowExclusiveLock
);
4309 * Disallow ALTER TABLE (and similar commands) when the current backend has
4310 * any open reference to the target table besides the one just acquired by
4311 * the calling command; this implies there's an open cursor or active plan.
4312 * We need this check because our lock doesn't protect us against stomping
4313 * on our own foot, only other people's feet!
4315 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4316 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4317 * possibly be relaxed to only error out for certain types of alterations.
4318 * But the use-case for allowing any of these things is not obvious, so we
4319 * won't work hard at it for now.
4321 * We also reject these commands if there are any pending AFTER trigger events
4322 * for the rel. This is certainly necessary for the rewriting variants of
4323 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4324 * events would try to fetch the wrong tuples. It might be overly cautious
4325 * in other cases, but again it seems better to err on the side of paranoia.
4327 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4328 * we are worried about active indexscans on the index. The trigger-event
4329 * check can be skipped, since we are doing no damage to the parent table.
4331 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4334 CheckTableNotInUse(Relation rel
, const char *stmt
)
4336 int expected_refcnt
;
4338 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4339 if (rel
->rd_refcnt
!= expected_refcnt
)
4341 (errcode(ERRCODE_OBJECT_IN_USE
),
4342 /* translator: first %s is a SQL command, eg ALTER TABLE */
4343 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4344 stmt
, RelationGetRelationName(rel
))));
4346 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4347 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4348 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4350 (errcode(ERRCODE_OBJECT_IN_USE
),
4351 /* translator: first %s is a SQL command, eg ALTER TABLE */
4352 errmsg("cannot %s \"%s\" because it has pending trigger events",
4353 stmt
, RelationGetRelationName(rel
))));
4357 * AlterTableLookupRelation
4358 * Look up, and lock, the OID for the relation named by an alter table
4362 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4364 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4365 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4366 RangeVarCallbackForAlterRelation
,
4372 * Execute ALTER TABLE, which can be a list of subcommands
4374 * ALTER TABLE is performed in three phases:
4375 * 1. Examine subcommands and perform pre-transformation checking.
4376 * 2. Validate and transform subcommands, and update system catalogs.
4377 * 3. Scan table(s) to check new constraints, and optionally recopy
4378 * the data into new table(s).
4379 * Phase 3 is not performed unless one or more of the subcommands requires
4380 * it. The intention of this design is to allow multiple independent
4381 * updates of the table schema to be performed with only one pass over the
4384 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4385 * each table to be affected (there may be multiple affected tables if the
4386 * commands traverse a table inheritance hierarchy). Also we do preliminary
4387 * validation of the subcommands. Because earlier subcommands may change
4388 * the catalog state seen by later commands, there are limits to what can
4389 * be done in this phase. Generally, this phase acquires table locks,
4390 * checks permissions and relkind, and recurses to find child tables.
4392 * ATRewriteCatalogs performs phase 2 for each affected table.
4393 * Certain subcommands need to be performed before others to avoid
4394 * unnecessary conflicts; for example, DROP COLUMN should come before
4395 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4396 * lists, one for each logical "pass" of phase 2.
4398 * ATRewriteTables performs phase 3 for those tables that need it.
4400 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4401 * since phase 1 already does it. However, for certain subcommand types
4402 * it is only possible to determine how to recurse at phase 2 time; for
4403 * those cases, phase 1 sets the cmd->recurse flag.
4405 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4406 * the whole operation; we don't have to do anything special to clean up.
4408 * The caller must lock the relation, with an appropriate lock level
4409 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4410 * or higher. We pass the lock level down
4411 * so that we can apply it recursively to inherited tables. Note that the
4412 * lock level we want as we recurse might well be higher than required for
4413 * that specific subcommand. So we pass down the overall lock requirement,
4414 * rather than reassess it at lower levels.
4416 * The caller also provides a "context" which is to be passed back to
4417 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4418 * Some of the fields therein, such as the relid, are used here as well.
4421 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4422 AlterTableUtilityContext
*context
)
4426 /* Caller is required to provide an adequate lock. */
4427 rel
= relation_open(context
->relid
, NoLock
);
4429 CheckTableNotInUse(rel
, "ALTER TABLE");
4431 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4435 * AlterTableInternal
4437 * ALTER TABLE with target specified by OID
4439 * We do not reject if the relation is already open, because it's quite
4440 * likely that one or more layers of caller have it open. That means it
4441 * is unsafe to use this entry point for alterations that could break
4442 * existing query plans. On the assumption it's not used for such, we
4443 * don't have to reject pending AFTER triggers, either.
4445 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4446 * used for any subcommand types that require parse transformation or
4447 * could generate subcommands that have to be passed to ProcessUtility.
4450 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4453 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4455 rel
= relation_open(relid
, lockmode
);
4457 EventTriggerAlterTableRelid(relid
);
4459 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4463 * AlterTableGetLockLevel
4465 * Sets the overall lock level required for the supplied list of subcommands.
4466 * Policy for doing this set according to needs of AlterTable(), see
4467 * comments there for overall explanation.
4469 * Function is called before and after parsing, so it must give same
4470 * answer each time it is called. Some subcommands are transformed
4471 * into other subcommand types, so the transform must never be made to a
4472 * lower lock level than previously assigned. All transforms are noted below.
4474 * Since this is called before we lock the table we cannot use table metadata
4475 * to influence the type of lock we acquire.
4477 * There should be no lockmodes hardcoded into the subcommand functions. All
4478 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4479 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4480 * and does not travel through this section of code and cannot be combined with
4481 * any of the subcommands given here.
4483 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4484 * so any changes that might affect SELECTs running on standbys need to use
4485 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4486 * have a solution for that also.
4488 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4489 * that takes a lock less than AccessExclusiveLock can change object definitions
4490 * while pg_dump is running. Be careful to check that the appropriate data is
4491 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4492 * otherwise we might end up with an inconsistent dump that can't restore.
4495 AlterTableGetLockLevel(List
*cmds
)
4498 * This only works if we read catalog tables using MVCC snapshots.
4501 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4505 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4506 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4508 switch (cmd
->subtype
)
4511 * These subcommands rewrite the heap, so require full locks.
4513 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4515 case AT_SetAccessMethod
: /* must rewrite heap */
4516 case AT_SetTableSpace
: /* must rewrite heap */
4517 case AT_AlterColumnType
: /* must rewrite heap */
4518 cmd_lockmode
= AccessExclusiveLock
;
4522 * These subcommands may require addition of toast tables. If
4523 * we add a toast table to a table currently being scanned, we
4524 * might miss data added to the new toast table by concurrent
4525 * insert transactions.
4527 case AT_SetStorage
: /* may add toast tables, see
4528 * ATRewriteCatalogs() */
4529 cmd_lockmode
= AccessExclusiveLock
;
4533 * Removing constraints can affect SELECTs that have been
4534 * optimized assuming the constraint holds true. See also
4535 * CloneFkReferenced.
4537 case AT_DropConstraint
: /* as DROP INDEX */
4538 case AT_DropNotNull
: /* may change some SQL plans */
4539 cmd_lockmode
= AccessExclusiveLock
;
4543 * Subcommands that may be visible to concurrent SELECTs
4545 case AT_DropColumn
: /* change visible to SELECT */
4546 case AT_AddColumnToView
: /* CREATE VIEW */
4547 case AT_DropOids
: /* used to equiv to DropColumn */
4548 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4549 case AT_EnableReplicaRule
: /* may change SELECT rules */
4550 case AT_EnableRule
: /* may change SELECT rules */
4551 case AT_DisableRule
: /* may change SELECT rules */
4552 cmd_lockmode
= AccessExclusiveLock
;
4556 * Changing owner may remove implicit SELECT privileges
4558 case AT_ChangeOwner
: /* change visible to SELECT */
4559 cmd_lockmode
= AccessExclusiveLock
;
4563 * Changing foreign table options may affect optimization.
4565 case AT_GenericOptions
:
4566 case AT_AlterColumnGenericOptions
:
4567 cmd_lockmode
= AccessExclusiveLock
;
4571 * These subcommands affect write operations only.
4574 case AT_EnableAlwaysTrig
:
4575 case AT_EnableReplicaTrig
:
4576 case AT_EnableTrigAll
:
4577 case AT_EnableTrigUser
:
4578 case AT_DisableTrig
:
4579 case AT_DisableTrigAll
:
4580 case AT_DisableTrigUser
:
4581 cmd_lockmode
= ShareRowExclusiveLock
;
4585 * These subcommands affect write operations only. XXX
4586 * Theoretically, these could be ShareRowExclusiveLock.
4588 case AT_ColumnDefault
:
4589 case AT_CookedColumnDefault
:
4590 case AT_AlterConstraint
:
4591 case AT_AddIndex
: /* from ADD CONSTRAINT */
4592 case AT_AddIndexConstraint
:
4593 case AT_ReplicaIdentity
:
4595 case AT_SetAttNotNull
:
4596 case AT_EnableRowSecurity
:
4597 case AT_DisableRowSecurity
:
4598 case AT_ForceRowSecurity
:
4599 case AT_NoForceRowSecurity
:
4600 case AT_AddIdentity
:
4601 case AT_DropIdentity
:
4602 case AT_SetIdentity
:
4603 case AT_SetExpression
:
4604 case AT_DropExpression
:
4605 case AT_SetCompression
:
4606 cmd_lockmode
= AccessExclusiveLock
;
4609 case AT_AddConstraint
:
4610 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4611 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4612 if (IsA(cmd
->def
, Constraint
))
4614 Constraint
*con
= (Constraint
*) cmd
->def
;
4616 switch (con
->contype
)
4618 case CONSTR_EXCLUSION
:
4619 case CONSTR_PRIMARY
:
4623 * Cases essentially the same as CREATE INDEX. We
4624 * could reduce the lock strength to ShareLock if
4625 * we can work out how to allow concurrent catalog
4626 * updates. XXX Might be set down to
4627 * ShareRowExclusiveLock but requires further
4630 cmd_lockmode
= AccessExclusiveLock
;
4632 case CONSTR_FOREIGN
:
4635 * We add triggers to both tables when we add a
4636 * Foreign Key, so the lock level must be at least
4637 * as strong as CREATE TRIGGER.
4639 cmd_lockmode
= ShareRowExclusiveLock
;
4643 cmd_lockmode
= AccessExclusiveLock
;
4649 * These subcommands affect inheritance behaviour. Queries
4650 * started before us will continue to see the old inheritance
4651 * behaviour, while queries started after we commit will see
4652 * new behaviour. No need to prevent reads or writes to the
4653 * subtable while we hook it up though. Changing the TupDesc
4654 * may be a problem, so keep highest lock.
4657 case AT_DropInherit
:
4658 cmd_lockmode
= AccessExclusiveLock
;
4662 * These subcommands affect implicit row type conversion. They
4663 * have affects similar to CREATE/DROP CAST on queries. don't
4664 * provide for invalidating parse trees as a result of such
4665 * changes, so we keep these at AccessExclusiveLock.
4669 cmd_lockmode
= AccessExclusiveLock
;
4673 * Only used by CREATE OR REPLACE VIEW which must conflict
4674 * with an SELECTs currently using the view.
4676 case AT_ReplaceRelOptions
:
4677 cmd_lockmode
= AccessExclusiveLock
;
4681 * These subcommands affect general strategies for performance
4682 * and maintenance, though don't change the semantic results
4683 * from normal data reads and writes. Delaying an ALTER TABLE
4684 * behind currently active writes only delays the point where
4685 * the new strategy begins to take effect, so there is no
4686 * benefit in waiting. In this case the minimum restriction
4687 * applies: we don't currently allow concurrent catalog
4690 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4691 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4692 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4693 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4694 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4695 cmd_lockmode
= ShareUpdateExclusiveLock
;
4699 case AT_SetUnLogged
:
4700 cmd_lockmode
= AccessExclusiveLock
;
4703 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4704 cmd_lockmode
= ShareUpdateExclusiveLock
;
4708 * Rel options are more complex than first appears. Options
4709 * are set here for tables, views and indexes; for historical
4710 * reasons these can all be used with ALTER TABLE, so we can't
4711 * decide between them using the basic grammar.
4713 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4715 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4717 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4720 case AT_AttachPartition
:
4721 cmd_lockmode
= ShareUpdateExclusiveLock
;
4724 case AT_DetachPartition
:
4725 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4726 cmd_lockmode
= ShareUpdateExclusiveLock
;
4728 cmd_lockmode
= AccessExclusiveLock
;
4731 case AT_DetachPartitionFinalize
:
4732 cmd_lockmode
= ShareUpdateExclusiveLock
;
4736 elog(ERROR
, "unrecognized alter table type: %d",
4737 (int) cmd
->subtype
);
4742 * Take the greatest lockmode from any subcommand
4744 if (cmd_lockmode
> lockmode
)
4745 lockmode
= cmd_lockmode
;
4752 * ATController provides top level control over the phases.
4754 * parsetree is passed in to allow it to be passed to event triggers
4758 ATController(AlterTableStmt
*parsetree
,
4759 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4760 AlterTableUtilityContext
*context
)
4765 /* Phase 1: preliminary examination of commands, create work queue */
4768 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4770 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4773 /* Close the relation, but keep lock until commit */
4774 relation_close(rel
, NoLock
);
4776 /* Phase 2: update system catalogs */
4777 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4779 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4780 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4786 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4787 * recursion and permission checks.
4789 * Caller must have acquired appropriate lock type on relation already.
4790 * This lock should be held until commit.
4793 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4794 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4795 AlterTableUtilityContext
*context
)
4797 AlteredTableInfo
*tab
;
4798 AlterTablePass pass
= AT_PASS_UNSET
;
4800 /* Find or create work queue entry for this table */
4801 tab
= ATGetQueueEntry(wqueue
, rel
);
4804 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4805 * partitions that are pending detach.
4807 if (rel
->rd_rel
->relispartition
&&
4808 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4809 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4811 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4812 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4813 RelationGetRelationName(rel
)),
4814 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4817 * Copy the original subcommand for each table, so we can scribble on it.
4818 * This avoids conflicts when different child tables need to make
4819 * different parse transformations (for example, the same column may have
4820 * different column numbers in different children).
4822 cmd
= copyObject(cmd
);
4825 * Do permissions and relkind checking, recursion to child tables if
4826 * needed, and any additional phase-1 processing needed. (But beware of
4827 * adding any processing that looks at table details that another
4828 * subcommand could change. In some cases we reject multiple subcommands
4829 * that could try to change the same state in contrary ways.)
4831 switch (cmd
->subtype
)
4833 case AT_AddColumn
: /* ADD COLUMN */
4834 ATSimplePermissions(cmd
->subtype
, rel
,
4835 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4836 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4838 /* Recursion occurs during execution phase */
4839 pass
= AT_PASS_ADD_COL
;
4841 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4842 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4843 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4845 /* Recursion occurs during execution phase */
4846 pass
= AT_PASS_ADD_COL
;
4848 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4851 * We allow defaults on views so that INSERT into a view can have
4852 * default-ish behavior. This works because the rewriter
4853 * substitutes default values into INSERTs before it expands
4856 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4857 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4858 /* No command-specific prep needed */
4859 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4861 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4862 /* This is currently used only in CREATE TABLE */
4863 /* (so the permission check really isn't necessary) */
4864 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4865 /* This command never recurses */
4866 pass
= AT_PASS_ADD_OTHERCONSTR
;
4868 case AT_AddIdentity
:
4869 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4870 /* Set up recursion for phase 2; no other prep needed */
4872 cmd
->recurse
= true;
4873 pass
= AT_PASS_ADD_OTHERCONSTR
;
4875 case AT_SetIdentity
:
4876 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4877 /* Set up recursion for phase 2; no other prep needed */
4879 cmd
->recurse
= true;
4880 /* This should run after AddIdentity, so do it in MISC pass */
4881 pass
= AT_PASS_MISC
;
4883 case AT_DropIdentity
:
4884 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4885 /* Set up recursion for phase 2; no other prep needed */
4887 cmd
->recurse
= true;
4888 pass
= AT_PASS_DROP
;
4890 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4891 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4892 /* Set up recursion for phase 2; no other prep needed */
4894 cmd
->recurse
= true;
4895 pass
= AT_PASS_DROP
;
4897 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4898 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4899 /* Set up recursion for phase 2; no other prep needed */
4901 cmd
->recurse
= true;
4902 pass
= AT_PASS_COL_ATTRS
;
4904 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull without adding
4906 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4907 /* Need command-specific recursion decision */
4908 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4909 pass
= AT_PASS_COL_ATTRS
;
4911 case AT_SetExpression
: /* ALTER COLUMN SET EXPRESSION */
4912 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4913 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4914 pass
= AT_PASS_SET_EXPRESSION
;
4916 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4917 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4918 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4919 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4920 pass
= AT_PASS_DROP
;
4922 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4923 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4924 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4925 /* No command-specific prep needed */
4926 pass
= AT_PASS_MISC
;
4928 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4929 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4930 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4931 /* This command never recurses */
4932 pass
= AT_PASS_MISC
;
4934 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4935 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4936 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4937 /* No command-specific prep needed */
4938 pass
= AT_PASS_MISC
;
4940 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4941 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4942 /* This command never recurses */
4943 /* No command-specific prep needed */
4944 pass
= AT_PASS_MISC
;
4946 case AT_DropColumn
: /* DROP COLUMN */
4947 ATSimplePermissions(cmd
->subtype
, rel
,
4948 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4949 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
4951 /* Recursion occurs during execution phase */
4952 pass
= AT_PASS_DROP
;
4954 case AT_AddIndex
: /* ADD INDEX */
4955 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4956 /* This command never recurses */
4957 /* No command-specific prep needed */
4958 pass
= AT_PASS_ADD_INDEX
;
4960 case AT_AddConstraint
: /* ADD CONSTRAINT */
4961 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4962 /* Recursion occurs during execution phase */
4963 /* No command-specific prep needed except saving recurse flag */
4965 cmd
->recurse
= true;
4966 pass
= AT_PASS_ADD_CONSTR
;
4968 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
4969 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4970 /* This command never recurses */
4971 /* No command-specific prep needed */
4972 pass
= AT_PASS_ADD_INDEXCONSTR
;
4974 case AT_DropConstraint
: /* DROP CONSTRAINT */
4975 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4976 ATCheckPartitionsNotInUse(rel
, lockmode
);
4977 /* Other recursion occurs during execution phase */
4978 /* No command-specific prep needed except saving recurse flag */
4980 cmd
->recurse
= true;
4981 pass
= AT_PASS_DROP
;
4983 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
4984 ATSimplePermissions(cmd
->subtype
, rel
,
4985 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4986 /* See comments for ATPrepAlterColumnType */
4987 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
4988 AT_PASS_UNSET
, context
);
4989 Assert(cmd
!= NULL
);
4990 /* Performs own recursion */
4991 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
4993 pass
= AT_PASS_ALTER_TYPE
;
4995 case AT_AlterColumnGenericOptions
:
4996 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
4997 /* This command never recurses */
4998 /* No command-specific prep needed */
4999 pass
= AT_PASS_MISC
;
5001 case AT_ChangeOwner
: /* ALTER OWNER */
5002 /* This command never recurses */
5003 /* No command-specific prep needed */
5004 pass
= AT_PASS_MISC
;
5006 case AT_ClusterOn
: /* CLUSTER ON */
5007 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5008 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5009 /* These commands never recurse */
5010 /* No command-specific prep needed */
5011 pass
= AT_PASS_MISC
;
5013 case AT_SetLogged
: /* SET LOGGED */
5014 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
5015 if (tab
->chgPersistence
)
5017 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5018 errmsg("cannot change persistence setting twice")));
5019 tab
->chgPersistence
= ATPrepChangePersistence(rel
, true);
5020 /* force rewrite if necessary; see comment in ATRewriteTables */
5021 if (tab
->chgPersistence
)
5023 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
5024 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
5026 pass
= AT_PASS_MISC
;
5028 case AT_SetUnLogged
: /* SET UNLOGGED */
5029 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
5030 if (tab
->chgPersistence
)
5032 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5033 errmsg("cannot change persistence setting twice")));
5034 tab
->chgPersistence
= ATPrepChangePersistence(rel
, false);
5035 /* force rewrite if necessary; see comment in ATRewriteTables */
5036 if (tab
->chgPersistence
)
5038 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
5039 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
5041 pass
= AT_PASS_MISC
;
5043 case AT_DropOids
: /* SET WITHOUT OIDS */
5044 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5045 pass
= AT_PASS_DROP
;
5047 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5048 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5050 /* check if another access method change was already requested */
5051 if (tab
->chgAccessMethod
)
5053 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5054 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5056 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
5057 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
5059 case AT_SetTableSpace
: /* SET TABLESPACE */
5060 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
|
5061 ATT_PARTITIONED_INDEX
);
5062 /* This command never recurses */
5063 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
5064 pass
= AT_PASS_MISC
; /* doesn't actually matter */
5066 case AT_SetRelOptions
: /* SET (...) */
5067 case AT_ResetRelOptions
: /* RESET (...) */
5068 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
5069 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_MATVIEW
| ATT_INDEX
);
5070 /* This command never recurses */
5071 /* No command-specific prep needed */
5072 pass
= AT_PASS_MISC
;
5074 case AT_AddInherit
: /* INHERIT */
5075 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5076 /* This command never recurses */
5077 ATPrepAddInherit(rel
);
5078 pass
= AT_PASS_MISC
;
5080 case AT_DropInherit
: /* NO INHERIT */
5081 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5082 /* This command never recurses */
5083 /* No command-specific prep needed */
5084 pass
= AT_PASS_MISC
;
5086 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5087 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5088 /* Recursion occurs during execution phase */
5089 pass
= AT_PASS_MISC
;
5091 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5092 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5093 /* Recursion occurs during execution phase */
5094 /* No command-specific prep needed except saving recurse flag */
5096 cmd
->recurse
= true;
5097 pass
= AT_PASS_MISC
;
5099 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
5100 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5101 pass
= AT_PASS_MISC
;
5102 /* This command never recurses */
5103 /* No command-specific prep needed */
5105 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
5106 case AT_EnableAlwaysTrig
:
5107 case AT_EnableReplicaTrig
:
5108 case AT_EnableTrigAll
:
5109 case AT_EnableTrigUser
:
5110 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
5111 case AT_DisableTrigAll
:
5112 case AT_DisableTrigUser
:
5113 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5114 /* Set up recursion for phase 2; no other prep needed */
5116 cmd
->recurse
= true;
5117 pass
= AT_PASS_MISC
;
5119 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
5120 case AT_EnableAlwaysRule
:
5121 case AT_EnableReplicaRule
:
5122 case AT_DisableRule
:
5123 case AT_AddOf
: /* OF */
5124 case AT_DropOf
: /* NOT OF */
5125 case AT_EnableRowSecurity
:
5126 case AT_DisableRowSecurity
:
5127 case AT_ForceRowSecurity
:
5128 case AT_NoForceRowSecurity
:
5129 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5130 /* These commands never recurse */
5131 /* No command-specific prep needed */
5132 pass
= AT_PASS_MISC
;
5134 case AT_GenericOptions
:
5135 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5136 /* No command-specific prep needed */
5137 pass
= AT_PASS_MISC
;
5139 case AT_AttachPartition
:
5140 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_INDEX
);
5141 /* No command-specific prep needed */
5142 pass
= AT_PASS_MISC
;
5144 case AT_DetachPartition
:
5145 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5146 /* No command-specific prep needed */
5147 pass
= AT_PASS_MISC
;
5149 case AT_DetachPartitionFinalize
:
5150 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5151 /* No command-specific prep needed */
5152 pass
= AT_PASS_MISC
;
5155 elog(ERROR
, "unrecognized alter table type: %d",
5156 (int) cmd
->subtype
);
5157 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
5160 Assert(pass
> AT_PASS_UNSET
);
5162 /* Add the subcommand to the appropriate list for phase 2 */
5163 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
5169 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5170 * dispatched in a "safe" execution order (designed to avoid unnecessary
5174 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
5175 AlterTableUtilityContext
*context
)
5180 * We process all the tables "in parallel", one pass at a time. This is
5181 * needed because we may have to propagate work from one table to another
5182 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5183 * re-adding of the foreign key constraint to the other table). Work can
5184 * only be propagated into later passes, however.
5186 for (AlterTablePass pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
5188 /* Go through each table that needs to be processed */
5189 foreach(ltab
, *wqueue
)
5191 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5192 List
*subcmds
= tab
->subcmds
[pass
];
5199 * Open the relation and store it in tab. This allows subroutines
5200 * close and reopen, if necessary. Appropriate lock was obtained
5201 * by phase 1, needn't get it again.
5203 tab
->rel
= relation_open(tab
->relid
, NoLock
);
5205 foreach(lcmd
, subcmds
)
5206 ATExecCmd(wqueue
, tab
,
5207 lfirst_node(AlterTableCmd
, lcmd
),
5208 lockmode
, pass
, context
);
5211 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5212 * (this is not done in ATExecAlterColumnType since it should be
5213 * done only once if multiple columns of a table are altered).
5215 if (pass
== AT_PASS_ALTER_TYPE
|| pass
== AT_PASS_SET_EXPRESSION
)
5216 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
5220 relation_close(tab
->rel
, NoLock
);
5226 /* Check to see if a toast table must be added. */
5227 foreach(ltab
, *wqueue
)
5229 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5232 * If the table is source table of ATTACH PARTITION command, we did
5233 * not modify anything about it that will change its toasting
5234 * requirement, so no need to check.
5236 if (((tab
->relkind
== RELKIND_RELATION
||
5237 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5238 tab
->partition_constraint
== NULL
) ||
5239 tab
->relkind
== RELKIND_MATVIEW
)
5240 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5245 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5248 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5249 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
5250 AlterTableUtilityContext
*context
)
5252 ObjectAddress address
= InvalidObjectAddress
;
5253 Relation rel
= tab
->rel
;
5255 switch (cmd
->subtype
)
5257 case AT_AddColumn
: /* ADD COLUMN */
5258 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5259 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5260 cmd
->recurse
, false,
5261 lockmode
, cur_pass
, context
);
5263 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5264 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5266 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5267 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5269 case AT_AddIdentity
:
5270 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5272 Assert(cmd
!= NULL
);
5273 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5275 case AT_SetIdentity
:
5276 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5278 Assert(cmd
!= NULL
);
5279 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5281 case AT_DropIdentity
:
5282 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
, cmd
->recurse
, false);
5284 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5285 address
= ATExecDropNotNull(rel
, cmd
->name
, cmd
->recurse
, lockmode
);
5287 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5288 address
= ATExecSetNotNull(wqueue
, rel
, NULL
, cmd
->name
,
5289 cmd
->recurse
, false, NULL
, lockmode
);
5291 case AT_SetAttNotNull
: /* set pg_attribute.attnotnull */
5292 address
= ATExecSetAttNotNull(wqueue
, rel
, cmd
->name
, lockmode
);
5294 case AT_SetExpression
:
5295 address
= ATExecSetExpression(tab
, rel
, cmd
->name
, cmd
->def
, lockmode
);
5297 case AT_DropExpression
:
5298 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5300 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5301 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5303 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5304 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5306 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5307 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5309 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5310 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5312 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5313 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5316 case AT_DropColumn
: /* DROP COLUMN */
5317 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5318 cmd
->behavior
, cmd
->recurse
, false,
5319 cmd
->missing_ok
, lockmode
,
5322 case AT_AddIndex
: /* ADD INDEX */
5323 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5326 case AT_ReAddIndex
: /* ADD INDEX */
5327 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5330 case AT_ReAddStatistics
: /* ADD STATISTICS */
5331 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5334 case AT_AddConstraint
: /* ADD CONSTRAINT */
5335 /* Transform the command only during initial examination */
5336 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5337 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5338 cmd
->recurse
, lockmode
,
5340 /* Depending on constraint type, might be no more work to do now */
5343 ATExecAddConstraint(wqueue
, tab
, rel
,
5344 (Constraint
*) cmd
->def
,
5345 cmd
->recurse
, false, lockmode
);
5347 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5349 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5350 true, true, lockmode
);
5352 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5355 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5356 ((AlterDomainStmt
*) cmd
->def
)->def
,
5359 case AT_ReAddComment
: /* Re-add existing comment */
5360 address
= CommentObject((CommentStmt
*) cmd
->def
);
5362 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5363 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5366 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5367 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5369 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5370 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5373 case AT_DropConstraint
: /* DROP CONSTRAINT */
5374 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5376 cmd
->missing_ok
, lockmode
);
5378 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5379 /* parse transformation was done earlier */
5380 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5382 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5384 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5385 (List
*) cmd
->def
, lockmode
);
5387 case AT_ChangeOwner
: /* ALTER OWNER */
5388 ATExecChangeOwner(RelationGetRelid(rel
),
5389 get_rolespec_oid(cmd
->newowner
, false),
5392 case AT_ClusterOn
: /* CLUSTER ON */
5393 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5395 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5396 ATExecDropCluster(rel
, lockmode
);
5398 case AT_SetLogged
: /* SET LOGGED */
5399 case AT_SetUnLogged
: /* SET UNLOGGED */
5401 case AT_DropOids
: /* SET WITHOUT OIDS */
5402 /* nothing to do here, oid columns don't exist anymore */
5404 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5407 * Only do this for partitioned tables, for which this is just a
5408 * catalog change. Tables with storage are handled by Phase 3.
5410 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
5411 tab
->chgAccessMethod
)
5412 ATExecSetAccessMethodNoStorage(rel
, tab
->newAccessMethod
);
5414 case AT_SetTableSpace
: /* SET TABLESPACE */
5417 * Only do this for partitioned tables and indexes, for which this
5418 * is just a catalog change. Other relation types which have
5419 * storage are handled by Phase 3.
5421 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5422 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5423 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5426 case AT_SetRelOptions
: /* SET (...) */
5427 case AT_ResetRelOptions
: /* RESET (...) */
5428 case AT_ReplaceRelOptions
: /* replace entire option list */
5429 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5431 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5432 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5433 TRIGGER_FIRES_ON_ORIGIN
, false,
5437 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5438 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5439 TRIGGER_FIRES_ALWAYS
, false,
5443 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5444 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5445 TRIGGER_FIRES_ON_REPLICA
, false,
5449 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5450 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5451 TRIGGER_DISABLED
, false,
5455 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5456 ATExecEnableDisableTrigger(rel
, NULL
,
5457 TRIGGER_FIRES_ON_ORIGIN
, false,
5461 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5462 ATExecEnableDisableTrigger(rel
, NULL
,
5463 TRIGGER_DISABLED
, false,
5467 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5468 ATExecEnableDisableTrigger(rel
, NULL
,
5469 TRIGGER_FIRES_ON_ORIGIN
, true,
5473 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5474 ATExecEnableDisableTrigger(rel
, NULL
,
5475 TRIGGER_DISABLED
, true,
5480 case AT_EnableRule
: /* ENABLE RULE name */
5481 ATExecEnableDisableRule(rel
, cmd
->name
,
5482 RULE_FIRES_ON_ORIGIN
, lockmode
);
5484 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5485 ATExecEnableDisableRule(rel
, cmd
->name
,
5486 RULE_FIRES_ALWAYS
, lockmode
);
5488 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5489 ATExecEnableDisableRule(rel
, cmd
->name
,
5490 RULE_FIRES_ON_REPLICA
, lockmode
);
5492 case AT_DisableRule
: /* DISABLE RULE name */
5493 ATExecEnableDisableRule(rel
, cmd
->name
,
5494 RULE_DISABLED
, lockmode
);
5498 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5500 case AT_DropInherit
:
5501 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5504 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5507 ATExecDropOf(rel
, lockmode
);
5509 case AT_ReplicaIdentity
:
5510 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5512 case AT_EnableRowSecurity
:
5513 ATExecSetRowSecurity(rel
, true);
5515 case AT_DisableRowSecurity
:
5516 ATExecSetRowSecurity(rel
, false);
5518 case AT_ForceRowSecurity
:
5519 ATExecForceNoForceRowSecurity(rel
, true);
5521 case AT_NoForceRowSecurity
:
5522 ATExecForceNoForceRowSecurity(rel
, false);
5524 case AT_GenericOptions
:
5525 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5527 case AT_AttachPartition
:
5528 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5530 Assert(cmd
!= NULL
);
5531 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5532 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5535 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5536 ((PartitionCmd
*) cmd
->def
)->name
);
5538 case AT_DetachPartition
:
5539 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5541 Assert(cmd
!= NULL
);
5542 /* ATPrepCmd ensures it must be a table */
5543 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5544 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5545 ((PartitionCmd
*) cmd
->def
)->name
,
5546 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5548 case AT_DetachPartitionFinalize
:
5549 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5552 elog(ERROR
, "unrecognized alter table type: %d",
5553 (int) cmd
->subtype
);
5558 * Report the subcommand to interested event triggers.
5561 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5564 * Bump the command counter to ensure the next subcommand in the sequence
5565 * can see the changes so far
5567 CommandCounterIncrement();
5571 * ATParseTransformCmd: perform parse transformation for one subcommand
5573 * Returns the transformed subcommand tree, if there is one, else NULL.
5575 * The parser may hand back additional AlterTableCmd(s) and/or other
5576 * utility statements, either before or after the original subcommand.
5577 * Other AlterTableCmds are scheduled into the appropriate slot of the
5578 * AlteredTableInfo (they had better be for later passes than the current one).
5579 * Utility statements that are supposed to happen before the AlterTableCmd
5580 * are executed immediately. Those that are supposed to happen afterwards
5581 * are added to the tab->afterStmts list to be done at the very end.
5583 static AlterTableCmd
*
5584 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5585 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5586 AlterTablePass cur_pass
, AlterTableUtilityContext
*context
)
5588 AlterTableCmd
*newcmd
= NULL
;
5589 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5594 /* Gin up an AlterTableStmt with just this subcommand and this table */
5596 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5597 pstrdup(RelationGetRelationName(rel
)),
5599 atstmt
->relation
->inh
= recurse
;
5600 atstmt
->cmds
= list_make1(cmd
);
5601 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5602 atstmt
->missing_ok
= false;
5604 /* Transform the AlterTableStmt */
5605 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5607 context
->queryString
,
5611 /* Execute any statements that should happen before these subcommand(s) */
5612 foreach(lc
, beforeStmts
)
5614 Node
*stmt
= (Node
*) lfirst(lc
);
5616 ProcessUtilityForAlterTable(stmt
, context
);
5617 CommandCounterIncrement();
5620 /* Examine the transformed subcommands and schedule them appropriately */
5621 foreach(lc
, atstmt
->cmds
)
5623 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5624 AlterTablePass pass
;
5627 * This switch need only cover the subcommand types that can be added
5628 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5629 * executing the subcommand immediately, as a substitute for the
5630 * original subcommand. (Note, however, that this does cause
5631 * AT_AddConstraint subcommands to be rescheduled into later passes,
5632 * which is important for index and foreign key constraints.)
5634 * We assume we needn't do any phase-1 checks for added subcommands.
5636 switch (cmd2
->subtype
)
5638 case AT_SetAttNotNull
:
5639 ATSimpleRecursion(wqueue
, rel
, cmd2
, recurse
, lockmode
, context
);
5640 pass
= AT_PASS_COL_ATTRS
;
5645 * A primary key on a inheritance parent needs supporting NOT
5646 * NULL constraint on its children; enqueue commands to create
5647 * those or mark them inherited if they already exist.
5649 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5650 pass
= AT_PASS_ADD_INDEX
;
5652 case AT_AddIndexConstraint
:
5654 ATPrepAddPrimaryKey(wqueue
, rel
, cmd2
, lockmode
, context
);
5655 pass
= AT_PASS_ADD_INDEXCONSTR
;
5657 case AT_AddConstraint
:
5658 /* Recursion occurs during execution phase */
5660 cmd2
->recurse
= true;
5661 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5663 case CONSTR_PRIMARY
:
5665 case CONSTR_EXCLUSION
:
5666 pass
= AT_PASS_ADD_INDEXCONSTR
;
5669 pass
= AT_PASS_ADD_OTHERCONSTR
;
5673 case AT_AlterColumnGenericOptions
:
5674 /* This command never recurses */
5675 /* No command-specific prep needed */
5676 pass
= AT_PASS_MISC
;
5683 if (pass
< cur_pass
)
5685 /* Cannot schedule into a pass we already finished */
5686 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5689 else if (pass
> cur_pass
)
5691 /* OK, queue it up for later */
5692 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5697 * We should see at most one subcommand for the current pass,
5698 * which is the transformed version of the original subcommand.
5700 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5702 /* Found the transformed version of our subcommand */
5706 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5711 /* Queue up any after-statements to happen at the end */
5712 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5718 * ATRewriteTables: ALTER TABLE phase 3
5721 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5722 AlterTableUtilityContext
*context
)
5726 /* Go through each table that needs to be checked or rewritten */
5727 foreach(ltab
, *wqueue
)
5729 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5731 /* Relations without storage may be ignored here */
5732 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5736 * If we change column data types, the operation has to be propagated
5737 * to tables that use this table's rowtype as a column type.
5738 * tab->newvals will also be non-NULL in the case where we're adding a
5739 * column with a default. We choose to forbid that case as well,
5740 * since composite types might eventually support defaults.
5742 * (Eventually we'll probably need to check for composite type
5743 * dependencies even when we're just scanning the table without a
5744 * rewrite, but at the moment a composite type does not enforce any
5745 * constraints, so it's not necessary/appropriate to enforce them just
5748 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5752 rel
= table_open(tab
->relid
, NoLock
);
5753 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5754 table_close(rel
, NoLock
);
5758 * We only need to rewrite the table if at least one column needs to
5759 * be recomputed, or we are changing its persistence or access method.
5761 * There are two reasons for requiring a rewrite when changing
5762 * persistence: on one hand, we need to ensure that the buffers
5763 * belonging to each of the two relations are marked with or without
5764 * BM_PERMANENT properly. On the other hand, since rewriting creates
5765 * and assigns a new relfilenumber, we automatically create or drop an
5766 * init fork for the relation as appropriate.
5768 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5770 /* Build a temporary relation and copy data */
5773 Oid NewAccessMethod
;
5777 OldHeap
= table_open(tab
->relid
, NoLock
);
5780 * We don't support rewriting of system catalogs; there are too
5781 * many corner cases and too little benefit. In particular this
5782 * is certainly not going to work for mapped catalogs.
5784 if (IsSystemRelation(OldHeap
))
5786 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5787 errmsg("cannot rewrite system relation \"%s\"",
5788 RelationGetRelationName(OldHeap
))));
5790 if (RelationIsUsedAsCatalogTable(OldHeap
))
5792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5793 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5794 RelationGetRelationName(OldHeap
))));
5797 * Don't allow rewrite on temp tables of other backends ... their
5798 * local buffer manager is not going to cope.
5800 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5802 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5803 errmsg("cannot rewrite temporary tables of other sessions")));
5806 * Select destination tablespace (same as original unless user
5807 * requested a change)
5809 if (tab
->newTableSpace
)
5810 NewTableSpace
= tab
->newTableSpace
;
5812 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5815 * Select destination access method (same as original unless user
5816 * requested a change)
5818 if (tab
->chgAccessMethod
)
5819 NewAccessMethod
= tab
->newAccessMethod
;
5821 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5824 * Select persistence of transient table (same as original unless
5825 * user requested a change)
5827 persistence
= tab
->chgPersistence
?
5828 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5830 table_close(OldHeap
, NoLock
);
5833 * Fire off an Event Trigger now, before actually rewriting the
5836 * We don't support Event Trigger for nested commands anywhere,
5837 * here included, and parsetree is given NULL when coming from
5838 * AlterTableInternal.
5840 * And fire it only once.
5843 EventTriggerTableRewrite((Node
*) parsetree
,
5848 * Create transient table that will receive the modified data.
5850 * Ensure it is marked correctly as logged or unlogged. We have
5851 * to do this here so that buffers for the new relfilenumber will
5852 * have the right persistence set, and at the same time ensure
5853 * that the original filenumbers's buffers will get read in with
5854 * the correct setting (i.e. the original one). Otherwise a
5855 * rollback after the rewrite would possibly result with buffers
5856 * for the original filenumbers having the wrong persistence
5859 * NB: This relies on swap_relation_files() also swapping the
5860 * persistence. That wouldn't work for pg_class, but that can't be
5863 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5864 persistence
, lockmode
);
5867 * Copy the heap data into the new table with the desired
5868 * modifications, and test the current data within the table
5869 * against new constraints generated by ALTER TABLE commands.
5871 ATRewriteTable(tab
, OIDNewHeap
, lockmode
);
5874 * Swap the physical files of the old and new heaps, then rebuild
5875 * indexes and discard the old heap. We can use RecentXmin for
5876 * the table's new relfrozenxid because we rewrote all the tuples
5877 * in ATRewriteTable, so no older Xid remains in the table. Also,
5878 * we never try to swap toast tables by content, since we have no
5879 * interest in letting this code work on system catalogs.
5881 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5883 !OidIsValid(tab
->newTableSpace
),
5885 ReadNextMultiXactId(),
5888 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5890 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5892 if (tab
->chgPersistence
)
5893 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5898 * If required, test the current data within the table against new
5899 * constraints generated by ALTER TABLE commands, but don't
5902 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5903 tab
->partition_constraint
!= NULL
)
5904 ATRewriteTable(tab
, InvalidOid
, lockmode
);
5907 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5908 * just do a block-by-block copy.
5910 if (tab
->newTableSpace
)
5911 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5915 * Also change persistence of owned sequences, so that it matches the
5916 * table persistence.
5918 if (tab
->chgPersistence
)
5920 List
*seqlist
= getOwnedSequences(tab
->relid
);
5923 foreach(lc
, seqlist
)
5925 Oid seq_relid
= lfirst_oid(lc
);
5927 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5933 * Foreign key constraints are checked in a final pass, since (a) it's
5934 * generally best to examine each one separately, and (b) it's at least
5935 * theoretically possible that we have changed both relations of the
5936 * foreign key, and we'd better have finished both rewrites before we try
5937 * to read the tables.
5939 foreach(ltab
, *wqueue
)
5941 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5942 Relation rel
= NULL
;
5945 /* Relations without storage may be ignored here too */
5946 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5949 foreach(lcon
, tab
->constraints
)
5951 NewConstraint
*con
= lfirst(lcon
);
5953 if (con
->contype
== CONSTR_FOREIGN
)
5955 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
5960 /* Long since locked, no need for another */
5961 rel
= table_open(tab
->relid
, NoLock
);
5964 refrel
= table_open(con
->refrelid
, RowShareLock
);
5966 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
5969 con
->conwithperiod
);
5972 * No need to mark the constraint row as validated, we did
5973 * that when we inserted the row earlier.
5976 table_close(refrel
, NoLock
);
5981 table_close(rel
, NoLock
);
5984 /* Finally, run any afterStmts that were queued up */
5985 foreach(ltab
, *wqueue
)
5987 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5990 foreach(lc
, tab
->afterStmts
)
5992 Node
*stmt
= (Node
*) lfirst(lc
);
5994 ProcessUtilityForAlterTable(stmt
, context
);
5995 CommandCounterIncrement();
6001 * ATRewriteTable: scan or rewrite one table
6003 * OIDNewHeap is InvalidOid if we don't need to rewrite
6006 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
)
6010 TupleDesc oldTupDesc
;
6011 TupleDesc newTupDesc
;
6012 bool needscan
= false;
6013 List
*notnull_attrs
;
6018 BulkInsertState bistate
;
6020 ExprState
*partqualstate
= NULL
;
6023 * Open the relation(s). We have surely already locked the existing
6026 oldrel
= table_open(tab
->relid
, NoLock
);
6027 oldTupDesc
= tab
->oldDesc
;
6028 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
6030 if (OidIsValid(OIDNewHeap
))
6031 newrel
= table_open(OIDNewHeap
, lockmode
);
6036 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6037 * is empty, so don't bother using it.
6041 mycid
= GetCurrentCommandId(true);
6042 bistate
= GetBulkInsertState();
6043 ti_options
= TABLE_INSERT_SKIP_FSM
;
6047 /* keep compiler quiet about using these uninitialized */
6054 * Generate the constraint and default execution states
6057 estate
= CreateExecutorState();
6059 /* Build the needed expression execution states */
6060 foreach(l
, tab
->constraints
)
6062 NewConstraint
*con
= lfirst(l
);
6064 switch (con
->contype
)
6068 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
6070 case CONSTR_FOREIGN
:
6071 /* Nothing to do here */
6074 elog(ERROR
, "unrecognized constraint type: %d",
6075 (int) con
->contype
);
6079 /* Build expression execution states for partition check quals */
6080 if (tab
->partition_constraint
)
6083 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
6086 foreach(l
, tab
->newvals
)
6088 NewColumnValue
*ex
= lfirst(l
);
6090 /* expr already planned */
6091 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
6094 notnull_attrs
= NIL
;
6095 if (newrel
|| tab
->verify_new_notnull
)
6098 * If we are rebuilding the tuples OR if we added any new but not
6099 * verified not-null constraints, check all not-null constraints. This
6100 * is a bit of overkill but it minimizes risk of bugs, and
6101 * heap_attisnull is a pretty cheap test anyway.
6103 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6105 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
6107 if (attr
->attnotnull
&& !attr
->attisdropped
)
6108 notnull_attrs
= lappend_int(notnull_attrs
, i
);
6114 if (newrel
|| needscan
)
6116 ExprContext
*econtext
;
6117 TupleTableSlot
*oldslot
;
6118 TupleTableSlot
*newslot
;
6120 MemoryContext oldCxt
;
6121 List
*dropped_attrs
= NIL
;
6127 (errmsg_internal("rewriting table \"%s\"",
6128 RelationGetRelationName(oldrel
))));
6131 (errmsg_internal("verifying table \"%s\"",
6132 RelationGetRelationName(oldrel
))));
6137 * All predicate locks on the tuples or pages are about to be made
6138 * invalid, because we move tuples around. Promote them to
6141 TransferPredicateLocksToHeapRelation(oldrel
);
6144 econtext
= GetPerTupleExprContext(estate
);
6147 * Create necessary tuple slots. When rewriting, two slots are needed,
6148 * otherwise one suffices. In the case where one slot suffices, we
6149 * need to use the new tuple descriptor, otherwise some constraints
6150 * can't be evaluated. Note that even when the tuple layout is the
6151 * same and no rewrite is required, the tupDescs might not be
6152 * (consider ADD COLUMN without a default).
6156 Assert(newrel
!= NULL
);
6157 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
6158 table_slot_callbacks(oldrel
));
6159 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
6160 table_slot_callbacks(newrel
));
6163 * Set all columns in the new slot to NULL initially, to ensure
6164 * columns added as part of the rewrite are initialized to NULL.
6165 * That is necessary as tab->newvals will not contain an
6166 * expression for columns with a NULL default, e.g. when adding a
6167 * column without a default together with a column with a default
6168 * requiring an actual rewrite.
6170 ExecStoreAllNullTuple(newslot
);
6174 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
6175 table_slot_callbacks(oldrel
));
6180 * Any attributes that are dropped according to the new tuple
6181 * descriptor can be set to NULL. We precompute the list of dropped
6182 * attributes to avoid needing to do so in the per-tuple loop.
6184 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6186 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
6187 dropped_attrs
= lappend_int(dropped_attrs
, i
);
6191 * Scan through the rows, generating a new row if needed and then
6192 * checking all the constraints.
6194 snapshot
= RegisterSnapshot(GetLatestSnapshot());
6195 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
6198 * Switch to per-tuple memory context and reset it for each tuple
6199 * produced, so we don't leak memory.
6201 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
6203 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
6205 TupleTableSlot
*insertslot
;
6207 if (tab
->rewrite
> 0)
6209 /* Extract data from old tuple */
6210 slot_getallattrs(oldslot
);
6211 ExecClearTuple(newslot
);
6213 /* copy attributes */
6214 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
6215 sizeof(Datum
) * oldslot
->tts_nvalid
);
6216 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
6217 sizeof(bool) * oldslot
->tts_nvalid
);
6219 /* Set dropped attributes to null in new tuple */
6220 foreach(lc
, dropped_attrs
)
6221 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
6224 * Constraints and GENERATED expressions might reference the
6225 * tableoid column, so fill tts_tableOid with the desired
6226 * value. (We must do this each time, because it gets
6227 * overwritten with newrel's OID during storing.)
6229 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
6232 * Process supplied expressions to replace selected columns.
6234 * First, evaluate expressions whose inputs come from the old
6237 econtext
->ecxt_scantuple
= oldslot
;
6239 foreach(l
, tab
->newvals
)
6241 NewColumnValue
*ex
= lfirst(l
);
6243 if (ex
->is_generated
)
6246 newslot
->tts_values
[ex
->attnum
- 1]
6247 = ExecEvalExpr(ex
->exprstate
,
6249 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6252 ExecStoreVirtualTuple(newslot
);
6255 * Now, evaluate any expressions whose inputs come from the
6256 * new tuple. We assume these columns won't reference each
6257 * other, so that there's no ordering dependency.
6259 econtext
->ecxt_scantuple
= newslot
;
6261 foreach(l
, tab
->newvals
)
6263 NewColumnValue
*ex
= lfirst(l
);
6265 if (!ex
->is_generated
)
6268 newslot
->tts_values
[ex
->attnum
- 1]
6269 = ExecEvalExpr(ex
->exprstate
,
6271 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6274 insertslot
= newslot
;
6279 * If there's no rewrite, old and new table are guaranteed to
6280 * have the same AM, so we can just use the old slot to verify
6281 * new constraints etc.
6283 insertslot
= oldslot
;
6286 /* Now check any constraints on the possibly-changed tuple */
6287 econtext
->ecxt_scantuple
= insertslot
;
6289 foreach(l
, notnull_attrs
)
6291 int attn
= lfirst_int(l
);
6293 if (slot_attisnull(insertslot
, attn
+ 1))
6295 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6298 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6299 errmsg("column \"%s\" of relation \"%s\" contains null values",
6300 NameStr(attr
->attname
),
6301 RelationGetRelationName(oldrel
)),
6302 errtablecol(oldrel
, attn
+ 1)));
6306 foreach(l
, tab
->constraints
)
6308 NewConstraint
*con
= lfirst(l
);
6310 switch (con
->contype
)
6313 if (!ExecCheck(con
->qualstate
, econtext
))
6315 (errcode(ERRCODE_CHECK_VIOLATION
),
6316 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6318 RelationGetRelationName(oldrel
)),
6319 errtableconstraint(oldrel
, con
->name
)));
6321 case CONSTR_NOTNULL
:
6322 case CONSTR_FOREIGN
:
6323 /* Nothing to do here */
6326 elog(ERROR
, "unrecognized constraint type: %d",
6327 (int) con
->contype
);
6331 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6333 if (tab
->validate_default
)
6335 (errcode(ERRCODE_CHECK_VIOLATION
),
6336 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6337 RelationGetRelationName(oldrel
)),
6341 (errcode(ERRCODE_CHECK_VIOLATION
),
6342 errmsg("partition constraint of relation \"%s\" is violated by some row",
6343 RelationGetRelationName(oldrel
)),
6347 /* Write the tuple out to the new relation */
6349 table_tuple_insert(newrel
, insertslot
, mycid
,
6350 ti_options
, bistate
);
6352 ResetExprContext(econtext
);
6354 CHECK_FOR_INTERRUPTS();
6357 MemoryContextSwitchTo(oldCxt
);
6358 table_endscan(scan
);
6359 UnregisterSnapshot(snapshot
);
6361 ExecDropSingleTupleTableSlot(oldslot
);
6363 ExecDropSingleTupleTableSlot(newslot
);
6366 FreeExecutorState(estate
);
6368 table_close(oldrel
, NoLock
);
6371 FreeBulkInsertState(bistate
);
6373 table_finish_bulk_insert(newrel
, ti_options
);
6375 table_close(newrel
, NoLock
);
6380 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6382 static AlteredTableInfo
*
6383 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6385 Oid relid
= RelationGetRelid(rel
);
6386 AlteredTableInfo
*tab
;
6389 foreach(ltab
, *wqueue
)
6391 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6392 if (tab
->relid
== relid
)
6397 * Not there, so add it. Note that we make a copy of the relation's
6398 * existing descriptor before anything interesting can happen to it.
6400 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6402 tab
->rel
= NULL
; /* set later */
6403 tab
->relkind
= rel
->rd_rel
->relkind
;
6404 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6405 tab
->newAccessMethod
= InvalidOid
;
6406 tab
->chgAccessMethod
= false;
6407 tab
->newTableSpace
= InvalidOid
;
6408 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6409 tab
->chgPersistence
= false;
6411 *wqueue
= lappend(*wqueue
, tab
);
6417 alter_table_type_to_string(AlterTableType cmdtype
)
6422 case AT_AddColumnToView
:
6423 return "ADD COLUMN";
6424 case AT_ColumnDefault
:
6425 case AT_CookedColumnDefault
:
6426 return "ALTER COLUMN ... SET DEFAULT";
6427 case AT_DropNotNull
:
6428 return "ALTER COLUMN ... DROP NOT NULL";
6430 return "ALTER COLUMN ... SET NOT NULL";
6431 case AT_SetAttNotNull
:
6432 return NULL
; /* not real grammar */
6433 case AT_SetExpression
:
6434 return "ALTER COLUMN ... SET EXPRESSION";
6435 case AT_DropExpression
:
6436 return "ALTER COLUMN ... DROP EXPRESSION";
6437 case AT_SetStatistics
:
6438 return "ALTER COLUMN ... SET STATISTICS";
6440 return "ALTER COLUMN ... SET";
6441 case AT_ResetOptions
:
6442 return "ALTER COLUMN ... RESET";
6444 return "ALTER COLUMN ... SET STORAGE";
6445 case AT_SetCompression
:
6446 return "ALTER COLUMN ... SET COMPRESSION";
6448 return "DROP COLUMN";
6451 return NULL
; /* not real grammar */
6452 case AT_AddConstraint
:
6453 case AT_ReAddConstraint
:
6454 case AT_ReAddDomainConstraint
:
6455 case AT_AddIndexConstraint
:
6456 return "ADD CONSTRAINT";
6457 case AT_AlterConstraint
:
6458 return "ALTER CONSTRAINT";
6459 case AT_ValidateConstraint
:
6460 return "VALIDATE CONSTRAINT";
6461 case AT_DropConstraint
:
6462 return "DROP CONSTRAINT";
6463 case AT_ReAddComment
:
6464 return NULL
; /* not real grammar */
6465 case AT_AlterColumnType
:
6466 return "ALTER COLUMN ... SET DATA TYPE";
6467 case AT_AlterColumnGenericOptions
:
6468 return "ALTER COLUMN ... OPTIONS";
6469 case AT_ChangeOwner
:
6472 return "CLUSTER ON";
6473 case AT_DropCluster
:
6474 return "SET WITHOUT CLUSTER";
6475 case AT_SetAccessMethod
:
6476 return "SET ACCESS METHOD";
6478 return "SET LOGGED";
6479 case AT_SetUnLogged
:
6480 return "SET UNLOGGED";
6482 return "SET WITHOUT OIDS";
6483 case AT_SetTableSpace
:
6484 return "SET TABLESPACE";
6485 case AT_SetRelOptions
:
6487 case AT_ResetRelOptions
:
6489 case AT_ReplaceRelOptions
:
6490 return NULL
; /* not real grammar */
6492 return "ENABLE TRIGGER";
6493 case AT_EnableAlwaysTrig
:
6494 return "ENABLE ALWAYS TRIGGER";
6495 case AT_EnableReplicaTrig
:
6496 return "ENABLE REPLICA TRIGGER";
6497 case AT_DisableTrig
:
6498 return "DISABLE TRIGGER";
6499 case AT_EnableTrigAll
:
6500 return "ENABLE TRIGGER ALL";
6501 case AT_DisableTrigAll
:
6502 return "DISABLE TRIGGER ALL";
6503 case AT_EnableTrigUser
:
6504 return "ENABLE TRIGGER USER";
6505 case AT_DisableTrigUser
:
6506 return "DISABLE TRIGGER USER";
6508 return "ENABLE RULE";
6509 case AT_EnableAlwaysRule
:
6510 return "ENABLE ALWAYS RULE";
6511 case AT_EnableReplicaRule
:
6512 return "ENABLE REPLICA RULE";
6513 case AT_DisableRule
:
6514 return "DISABLE RULE";
6517 case AT_DropInherit
:
6518 return "NO INHERIT";
6523 case AT_ReplicaIdentity
:
6524 return "REPLICA IDENTITY";
6525 case AT_EnableRowSecurity
:
6526 return "ENABLE ROW SECURITY";
6527 case AT_DisableRowSecurity
:
6528 return "DISABLE ROW SECURITY";
6529 case AT_ForceRowSecurity
:
6530 return "FORCE ROW SECURITY";
6531 case AT_NoForceRowSecurity
:
6532 return "NO FORCE ROW SECURITY";
6533 case AT_GenericOptions
:
6535 case AT_AttachPartition
:
6536 return "ATTACH PARTITION";
6537 case AT_DetachPartition
:
6538 return "DETACH PARTITION";
6539 case AT_DetachPartitionFinalize
:
6540 return "DETACH PARTITION ... FINALIZE";
6541 case AT_AddIdentity
:
6542 return "ALTER COLUMN ... ADD IDENTITY";
6543 case AT_SetIdentity
:
6544 return "ALTER COLUMN ... SET";
6545 case AT_DropIdentity
:
6546 return "ALTER COLUMN ... DROP IDENTITY";
6547 case AT_ReAddStatistics
:
6548 return NULL
; /* not real grammar */
6555 * ATSimplePermissions
6557 * - Ensure that it is a relation (or possibly a view)
6558 * - Ensure this user is the owner
6559 * - Ensure that it is not a system table
6562 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6566 switch (rel
->rd_rel
->relkind
)
6568 case RELKIND_RELATION
:
6569 case RELKIND_PARTITIONED_TABLE
:
6570 actual_target
= ATT_TABLE
;
6573 actual_target
= ATT_VIEW
;
6575 case RELKIND_MATVIEW
:
6576 actual_target
= ATT_MATVIEW
;
6579 actual_target
= ATT_INDEX
;
6581 case RELKIND_PARTITIONED_INDEX
:
6582 actual_target
= ATT_PARTITIONED_INDEX
;
6584 case RELKIND_COMPOSITE_TYPE
:
6585 actual_target
= ATT_COMPOSITE_TYPE
;
6587 case RELKIND_FOREIGN_TABLE
:
6588 actual_target
= ATT_FOREIGN_TABLE
;
6590 case RELKIND_SEQUENCE
:
6591 actual_target
= ATT_SEQUENCE
;
6598 /* Wrong target type? */
6599 if ((actual_target
& allowed_targets
) == 0)
6601 const char *action_str
= alter_table_type_to_string(cmdtype
);
6605 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6606 /* translator: %s is a group of some SQL keywords */
6607 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6608 action_str
, RelationGetRelationName(rel
)),
6609 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6611 /* internal error? */
6612 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6613 RelationGetRelationName(rel
));
6616 /* Permissions checks */
6617 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6618 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6619 RelationGetRelationName(rel
));
6621 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6623 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6624 errmsg("permission denied: \"%s\" is a system catalog",
6625 RelationGetRelationName(rel
))));
6631 * Simple table recursion sufficient for most ALTER TABLE operations.
6632 * All direct and indirect children are processed in an unspecified order.
6633 * Note that if a child inherits from the original table via multiple
6634 * inheritance paths, it will be visited just once.
6637 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6638 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6639 AlterTableUtilityContext
*context
)
6642 * Propagate to children, if desired and if there are (or might be) any
6645 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6647 Oid relid
= RelationGetRelid(rel
);
6651 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6654 * find_all_inheritors does the recursive search of the inheritance
6655 * hierarchy, so all we have to do is process all of the relids in the
6656 * list that it returns.
6658 foreach(child
, children
)
6660 Oid childrelid
= lfirst_oid(child
);
6663 if (childrelid
== relid
)
6665 /* find_all_inheritors already got lock */
6666 childrel
= relation_open(childrelid
, NoLock
);
6667 CheckTableNotInUse(childrel
, "ALTER TABLE");
6668 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6669 relation_close(childrel
, NoLock
);
6675 * Obtain list of partitions of the given table, locking them all at the given
6676 * lockmode and ensuring that they all pass CheckTableNotInUse.
6678 * This function is a no-op if the given relation is not a partitioned table;
6679 * in particular, nothing is done if it's a legacy inheritance parent.
6682 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6684 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6689 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6690 /* first element is the parent rel; must ignore it */
6691 for_each_from(cell
, inh
, 1)
6695 /* find_all_inheritors already got lock */
6696 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6697 CheckTableNotInUse(childrel
, "ALTER TABLE");
6698 table_close(childrel
, NoLock
);
6705 * ATTypedTableRecursion
6707 * Propagate ALTER TYPE operations to the typed tables of that type.
6708 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6709 * recursion to inheritance children of the typed tables.
6712 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6713 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6718 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6720 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6721 RelationGetRelationName(rel
),
6724 foreach(child
, children
)
6726 Oid childrelid
= lfirst_oid(child
);
6729 childrel
= relation_open(childrelid
, lockmode
);
6730 CheckTableNotInUse(childrel
, "ALTER TABLE");
6731 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6732 relation_close(childrel
, NoLock
);
6738 * find_composite_type_dependencies
6740 * Check to see if the type "typeOid" is being used as a column in some table
6741 * (possibly nested several levels deep in composite types, arrays, etc!).
6742 * Eventually, we'd like to propagate the check or rewrite operation
6743 * into such tables, but for now, just error out if we find any.
6745 * Caller should provide either the associated relation of a rowtype,
6746 * or a type name (not both) for use in the error message, if any.
6748 * Note that "typeOid" is not necessarily a composite type; it could also be
6749 * another container type such as an array or range, or a domain over one of
6750 * these things. The name of this function is therefore somewhat historical,
6751 * but it's not worth changing.
6753 * We assume that functions and views depending on the type are not reasons
6754 * to reject the ALTER. (How safe is this really?)
6757 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6758 const char *origTypeName
)
6762 SysScanDesc depScan
;
6765 /* since this function recurses, it could be driven to stack overflow */
6766 check_stack_depth();
6769 * We scan pg_depend to find those things that depend on the given type.
6770 * (We assume we can ignore refobjsubid for a type.)
6772 depRel
= table_open(DependRelationId
, AccessShareLock
);
6774 ScanKeyInit(&key
[0],
6775 Anum_pg_depend_refclassid
,
6776 BTEqualStrategyNumber
, F_OIDEQ
,
6777 ObjectIdGetDatum(TypeRelationId
));
6778 ScanKeyInit(&key
[1],
6779 Anum_pg_depend_refobjid
,
6780 BTEqualStrategyNumber
, F_OIDEQ
,
6781 ObjectIdGetDatum(typeOid
));
6783 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6786 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6788 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6790 TupleDesc tupleDesc
;
6791 Form_pg_attribute att
;
6793 /* Check for directly dependent types */
6794 if (pg_depend
->classid
== TypeRelationId
)
6797 * This must be an array, domain, or range containing the given
6798 * type, so recursively check for uses of this type. Note that
6799 * any error message will mention the original type not the
6800 * container; this is intentional.
6802 find_composite_type_dependencies(pg_depend
->objid
,
6803 origRelation
, origTypeName
);
6807 /* Else, ignore dependees that aren't relations */
6808 if (pg_depend
->classid
!= RelationRelationId
)
6811 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6812 tupleDesc
= RelationGetDescr(rel
);
6815 * If objsubid identifies a specific column, refer to that in error
6816 * messages. Otherwise, search to see if there's a user column of the
6817 * type. (We assume system columns are never of interesting types.)
6818 * The search is needed because an index containing an expression
6819 * column of the target type will just be recorded as a whole-relation
6820 * dependency. If we do not find a column of the type, the dependency
6821 * must indicate that the type is transiently referenced in an index
6822 * expression but not stored on disk, which we assume is OK, just as
6823 * we do for references in views. (It could also be that the target
6824 * type is embedded in some container type that is stored in an index
6825 * column, but the previous recursion should catch such cases.)
6827 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6828 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6832 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6834 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6835 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6841 /* No such column, so assume OK */
6842 relation_close(rel
, AccessShareLock
);
6848 * We definitely should reject if the relation has storage. If it's
6849 * partitioned, then perhaps we don't have to reject: if there are
6850 * partitions then we'll fail when we find one, else there is no
6851 * stored data to worry about. However, it's possible that the type
6852 * change would affect conclusions about whether the type is sortable
6853 * or hashable and thus (if it's a partitioning column) break the
6854 * partitioning rule. For now, reject for partitioned rels too.
6856 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6857 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6861 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6862 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6864 RelationGetRelationName(rel
),
6865 NameStr(att
->attname
))));
6866 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6869 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6870 RelationGetRelationName(origRelation
),
6871 RelationGetRelationName(rel
),
6872 NameStr(att
->attname
))));
6873 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6875 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6876 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6877 RelationGetRelationName(origRelation
),
6878 RelationGetRelationName(rel
),
6879 NameStr(att
->attname
))));
6882 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6883 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6884 RelationGetRelationName(origRelation
),
6885 RelationGetRelationName(rel
),
6886 NameStr(att
->attname
))));
6888 else if (OidIsValid(rel
->rd_rel
->reltype
))
6891 * A view or composite type itself isn't a problem, but we must
6892 * recursively check for indirect dependencies via its rowtype.
6894 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6895 origRelation
, origTypeName
);
6898 relation_close(rel
, AccessShareLock
);
6901 systable_endscan(depScan
);
6903 relation_close(depRel
, AccessShareLock
);
6908 * find_typed_table_dependencies
6910 * Check to see if a composite type is being used as the type of a
6911 * typed table. Abort if any are found and behavior is RESTRICT.
6912 * Else return the list of tables.
6915 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6923 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6925 ScanKeyInit(&key
[0],
6926 Anum_pg_class_reloftype
,
6927 BTEqualStrategyNumber
, F_OIDEQ
,
6928 ObjectIdGetDatum(typeOid
));
6930 scan
= table_beginscan_catalog(classRel
, 1, key
);
6932 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6934 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6936 if (behavior
== DROP_RESTRICT
)
6938 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6939 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6941 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6943 result
= lappend_oid(result
, classform
->oid
);
6946 table_endscan(scan
);
6947 table_close(classRel
, AccessShareLock
);
6956 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6957 * isn't suitable, throw an error. Currently, we require that the type
6958 * originated with CREATE TYPE AS. We could support any row type, but doing so
6959 * would require handling a number of extra corner cases in the DDL commands.
6960 * (Also, allowing domain-over-composite would open up a can of worms about
6961 * whether and how the domain's constraints should apply to derived tables.)
6964 check_of_type(HeapTuple typetuple
)
6966 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
6967 bool typeOk
= false;
6969 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
6971 Relation typeRelation
;
6973 Assert(OidIsValid(typ
->typrelid
));
6974 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
6975 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6978 * Close the parent rel, but keep our AccessShareLock on it until xact
6979 * commit. That will prevent someone else from deleting or ALTERing
6980 * the type before the typed table creation/conversion commits.
6982 relation_close(typeRelation
, NoLock
);
6986 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6987 errmsg("type %s is not a composite type",
6988 format_type_be(typ
->oid
))));
6993 * ALTER TABLE ADD COLUMN
6995 * Adds an additional attribute to a relation making the assumption that
6996 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6997 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7000 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7001 * have to decide at runtime whether to recurse or not depending on whether we
7002 * actually add a column or merely merge with an existing column. (We can't
7003 * check this in a static pre-pass because it won't handle multiple inheritance
7004 * situations correctly.)
7007 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
7008 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
7009 AlterTableUtilityContext
*context
)
7011 if (rel
->rd_rel
->reloftype
&& !recursing
)
7013 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7014 errmsg("cannot add column to typed table")));
7016 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
7017 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
7019 if (recurse
&& !is_view
)
7020 cmd
->recurse
= true;
7024 * Add a column to a table. The return value is the address of the
7025 * new column in the parent relation.
7027 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7028 * copy (but that happens only after we check for IF NOT EXISTS).
7030 static ObjectAddress
7031 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
7032 AlterTableCmd
**cmd
, bool recurse
, bool recursing
,
7033 LOCKMODE lockmode
, AlterTablePass cur_pass
,
7034 AlterTableUtilityContext
*context
)
7036 Oid myrelid
= RelationGetRelid(rel
);
7037 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7038 bool if_not_exists
= (*cmd
)->missing_ok
;
7042 Form_pg_attribute attribute
;
7048 AlterTableCmd
*childcmd
;
7049 ObjectAddress address
;
7052 /* since this function recurses, it could be driven to stack overflow */
7053 check_stack_depth();
7055 /* At top level, permission check was done in ATPrepCmd, else do it */
7057 ATSimplePermissions((*cmd
)->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7059 if (rel
->rd_rel
->relispartition
&& !recursing
)
7061 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7062 errmsg("cannot add column to a partition")));
7064 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
7067 * Are we adding the column to a recursion child? If so, check whether to
7068 * merge with an existing definition for the column. If we do merge, we
7069 * must not recurse. Children will already have the column, and recursing
7070 * into them would mess up attinhcount.
7072 if (colDef
->inhcount
> 0)
7076 /* Does child already have a column by this name? */
7077 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
7078 if (HeapTupleIsValid(tuple
))
7080 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7085 /* Child column must match on type, typmod, and collation */
7086 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
7087 if (ctypeId
!= childatt
->atttypid
||
7088 ctypmod
!= childatt
->atttypmod
)
7090 (errcode(ERRCODE_DATATYPE_MISMATCH
),
7091 errmsg("child table \"%s\" has different type for column \"%s\"",
7092 RelationGetRelationName(rel
), colDef
->colname
)));
7093 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
7094 if (ccollid
!= childatt
->attcollation
)
7096 (errcode(ERRCODE_COLLATION_MISMATCH
),
7097 errmsg("child table \"%s\" has different collation for column \"%s\"",
7098 RelationGetRelationName(rel
), colDef
->colname
),
7099 errdetail("\"%s\" versus \"%s\"",
7100 get_collation_name(ccollid
),
7101 get_collation_name(childatt
->attcollation
))));
7103 /* Bump the existing child att's inhcount */
7104 childatt
->attinhcount
++;
7105 if (childatt
->attinhcount
< 0)
7107 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7108 errmsg("too many inheritance parents"));
7109 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
7111 heap_freetuple(tuple
);
7113 /* Inform the user about the merge */
7115 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7116 colDef
->colname
, RelationGetRelationName(rel
))));
7118 table_close(attrdesc
, RowExclusiveLock
);
7120 /* Make the child column change visible */
7121 CommandCounterIncrement();
7123 return InvalidObjectAddress
;
7127 /* skip if the name already exists and if_not_exists is true */
7128 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
7130 table_close(attrdesc
, RowExclusiveLock
);
7131 return InvalidObjectAddress
;
7135 * Okay, we need to add the column, so go ahead and do parse
7136 * transformation. This can result in queueing up, or even immediately
7137 * executing, subsidiary operations (such as creation of unique indexes);
7138 * so we mustn't do it until we have made the if_not_exists check.
7140 * When recursing, the command was already transformed and we needn't do
7141 * so again. Also, if context isn't given we can't transform. (That
7142 * currently happens only for AT_AddColumnToView; we expect that view.c
7143 * passed us a ColumnDef that doesn't need work.)
7145 if (context
!= NULL
&& !recursing
)
7147 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
7149 Assert(*cmd
!= NULL
);
7150 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7154 * Regular inheritance children are independent enough not to inherit the
7155 * identity column from parent hence cannot recursively add identity
7156 * column if the table has inheritance children.
7158 * Partitions, on the other hand, are integral part of a partitioned table
7159 * and inherit identity column. Hence propagate identity column down the
7160 * partition hierarchy.
7162 if (colDef
->identity
&&
7164 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
&&
7165 find_inheritance_children(myrelid
, NoLock
) != NIL
)
7167 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7168 errmsg("cannot recursively add identity column to table that has child tables")));
7170 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
7172 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
7173 if (!HeapTupleIsValid(reltup
))
7174 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
7175 relkind
= ((Form_pg_class
) GETSTRUCT(reltup
))->relkind
;
7177 /* Determine the new attribute's number */
7178 newattnum
= ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
+ 1;
7179 if (newattnum
> MaxHeapAttributeNumber
)
7181 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
7182 errmsg("tables can have at most %d columns",
7183 MaxHeapAttributeNumber
)));
7186 * Construct new attribute's pg_attribute entry.
7188 tupdesc
= BuildDescForRelation(list_make1(colDef
));
7190 attribute
= TupleDescAttr(tupdesc
, 0);
7192 /* Fix up attribute number */
7193 attribute
->attnum
= newattnum
;
7195 /* make sure datatype is legal for a column */
7196 CheckAttributeType(NameStr(attribute
->attname
), attribute
->atttypid
, attribute
->attcollation
,
7197 list_make1_oid(rel
->rd_rel
->reltype
),
7200 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
7202 table_close(attrdesc
, RowExclusiveLock
);
7205 * Update pg_class tuple as appropriate
7207 ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
= newattnum
;
7209 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
7211 heap_freetuple(reltup
);
7213 /* Post creation hook for new attribute */
7214 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
7216 table_close(pgclass
, RowExclusiveLock
);
7218 /* Make the attribute's catalog entry visible */
7219 CommandCounterIncrement();
7222 * Store the DEFAULT, if any, in the catalogs
7224 if (colDef
->raw_default
)
7226 RawColumnDefault
*rawEnt
;
7228 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7229 rawEnt
->attnum
= attribute
->attnum
;
7230 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7233 * Attempt to skip a complete table rewrite by storing the specified
7234 * DEFAULT value outside of the heap. This may be disabled inside
7235 * AddRelationNewConstraints if the optimization cannot be applied.
7237 rawEnt
->missingMode
= (!colDef
->generated
);
7239 rawEnt
->generated
= colDef
->generated
;
7242 * This function is intended for CREATE TABLE, so it processes a
7243 * _list_ of defaults, but we just do one.
7245 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7246 false, true, false, NULL
);
7248 /* Make the additional catalog changes visible */
7249 CommandCounterIncrement();
7252 * Did the request for a missing value work? If not we'll have to do a
7255 if (!rawEnt
->missingMode
)
7256 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7260 * Tell Phase 3 to fill in the default expression, if there is one.
7262 * If there is no default, Phase 3 doesn't have to do anything, because
7263 * that effectively means that the default is NULL. The heap tuple access
7264 * routines always check for attnum > # of attributes in tuple, and return
7265 * NULL if so, so without any modification of the tuple data we will get
7266 * the effect of NULL values in the new column.
7268 * An exception occurs when the new column is of a domain type: the domain
7269 * might have a not-null constraint, or a check constraint that indirectly
7270 * rejects nulls. If there are any domain constraints then we construct
7271 * an explicit NULL default value that will be passed through
7272 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7273 * rewriting the table which we really don't have to do, but the present
7274 * design of domain processing doesn't offer any simple way of checking
7275 * the constraints more directly.)
7277 * Note: we use build_column_default, and not just the cooked default
7278 * returned by AddRelationNewConstraints, so that the right thing happens
7279 * when a datatype's default applies.
7281 * Note: it might seem that this should happen at the end of Phase 2, so
7282 * that the effects of subsequent subcommands can be taken into account.
7283 * It's intentional that we do it now, though. The new column should be
7284 * filled according to what is said in the ADD COLUMN subcommand, so that
7285 * the effects are the same as if this subcommand had been run by itself
7286 * and the later subcommands had been issued in new ALTER TABLE commands.
7288 * We can skip this entirely for relations without storage, since Phase 3
7289 * is certainly not going to touch them. System attributes don't have
7290 * interesting defaults, either.
7292 if (RELKIND_HAS_STORAGE(relkind
))
7295 * For an identity column, we can't use build_column_default(),
7296 * because the sequence ownership isn't set yet. So do it manually.
7298 if (colDef
->identity
)
7300 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7302 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7303 nve
->typeId
= attribute
->atttypid
;
7305 defval
= (Expr
*) nve
;
7307 /* must do a rewrite for identity columns */
7308 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7311 defval
= (Expr
*) build_column_default(rel
, attribute
->attnum
);
7313 if (!defval
&& DomainHasConstraints(attribute
->atttypid
))
7319 baseTypeMod
= attribute
->atttypmod
;
7320 baseTypeId
= getBaseTypeAndTypmod(attribute
->atttypid
, &baseTypeMod
);
7321 baseTypeColl
= get_typcollation(baseTypeId
);
7322 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7323 defval
= (Expr
*) coerce_to_target_type(NULL
,
7326 attribute
->atttypid
,
7327 attribute
->atttypmod
,
7328 COERCION_ASSIGNMENT
,
7329 COERCE_IMPLICIT_CAST
,
7331 if (defval
== NULL
) /* should not happen */
7332 elog(ERROR
, "failed to coerce base type to domain");
7337 NewColumnValue
*newval
;
7339 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7340 newval
->attnum
= attribute
->attnum
;
7341 newval
->expr
= expression_planner(defval
);
7342 newval
->is_generated
= (colDef
->generated
!= '\0');
7344 tab
->newvals
= lappend(tab
->newvals
, newval
);
7347 if (DomainHasConstraints(attribute
->atttypid
))
7348 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7350 if (!TupleDescAttr(rel
->rd_att
, attribute
->attnum
- 1)->atthasmissing
)
7353 * If the new column is NOT NULL, and there is no missing value,
7354 * tell Phase 3 it needs to check for NULLs.
7356 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7361 * Add needed dependency entries for the new column.
7363 add_column_datatype_dependency(myrelid
, newattnum
, attribute
->atttypid
);
7364 add_column_collation_dependency(myrelid
, newattnum
, attribute
->attcollation
);
7367 * Propagate to children as appropriate. Unlike most other ALTER
7368 * routines, we have to do this one level of recursion at a time; we can't
7369 * use find_all_inheritors to do it in one pass.
7372 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7375 * If we are told not to recurse, there had better not be any child
7376 * tables; else the addition would put them out of step.
7378 if (children
&& !recurse
)
7380 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7381 errmsg("column must be added to child tables too")));
7383 /* Children should see column as singly inherited */
7386 childcmd
= copyObject(*cmd
);
7387 colDef
= castNode(ColumnDef
, childcmd
->def
);
7388 colDef
->inhcount
= 1;
7389 colDef
->is_local
= false;
7392 childcmd
= *cmd
; /* no need to copy again */
7394 foreach(child
, children
)
7396 Oid childrelid
= lfirst_oid(child
);
7398 AlteredTableInfo
*childtab
;
7400 /* find_inheritance_children already got lock */
7401 childrel
= table_open(childrelid
, NoLock
);
7402 CheckTableNotInUse(childrel
, "ALTER TABLE");
7404 /* Find or create work queue entry for this table */
7405 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7407 /* Recurse to child; return value is ignored */
7408 ATExecAddColumn(wqueue
, childtab
, childrel
,
7409 &childcmd
, recurse
, true,
7410 lockmode
, cur_pass
, context
);
7412 table_close(childrel
, NoLock
);
7415 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7420 * If a new or renamed column will collide with the name of an existing
7421 * column and if_not_exists is false then error out, else do nothing.
7424 check_for_column_name_collision(Relation rel
, const char *colname
,
7431 * this test is deliberately not attisdropped-aware, since if one tries to
7432 * add a column matching a dropped column name, it's gonna fail anyway.
7434 attTuple
= SearchSysCache2(ATTNAME
,
7435 ObjectIdGetDatum(RelationGetRelid(rel
)),
7436 PointerGetDatum(colname
));
7437 if (!HeapTupleIsValid(attTuple
))
7440 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7441 ReleaseSysCache(attTuple
);
7444 * We throw a different error message for conflicts with system column
7445 * names, since they are normally not shown and the user might otherwise
7446 * be confused about the reason for the conflict.
7450 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7451 errmsg("column name \"%s\" conflicts with a system column name",
7458 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7459 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7460 colname
, RelationGetRelationName(rel
))));
7465 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7466 errmsg("column \"%s\" of relation \"%s\" already exists",
7467 colname
, RelationGetRelationName(rel
))));
7474 * Install a column's dependency on its datatype.
7477 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7479 ObjectAddress myself
,
7482 myself
.classId
= RelationRelationId
;
7483 myself
.objectId
= relid
;
7484 myself
.objectSubId
= attnum
;
7485 referenced
.classId
= TypeRelationId
;
7486 referenced
.objectId
= typid
;
7487 referenced
.objectSubId
= 0;
7488 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7492 * Install a column's dependency on its collation.
7495 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7497 ObjectAddress myself
,
7500 /* We know the default collation is pinned, so don't bother recording it */
7501 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7503 myself
.classId
= RelationRelationId
;
7504 myself
.objectId
= relid
;
7505 myself
.objectSubId
= attnum
;
7506 referenced
.classId
= CollationRelationId
;
7507 referenced
.objectId
= collid
;
7508 referenced
.objectSubId
= 0;
7509 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7514 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7516 * Return the address of the modified column. If the column was already
7517 * nullable, InvalidObjectAddress is returned.
7519 static ObjectAddress
7520 ATExecDropNotNull(Relation rel
, const char *colName
, bool recurse
,
7525 Form_pg_attribute attTup
;
7528 ObjectAddress address
;
7532 * lookup the attribute
7534 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7536 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7537 if (!HeapTupleIsValid(tuple
))
7539 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7540 errmsg("column \"%s\" of relation \"%s\" does not exist",
7541 colName
, RelationGetRelationName(rel
))));
7542 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7543 attnum
= attTup
->attnum
;
7544 ObjectAddressSubSet(address
, RelationRelationId
,
7545 RelationGetRelid(rel
), attnum
);
7547 /* If the column is already nullable there's nothing to do. */
7548 if (!attTup
->attnotnull
)
7550 table_close(attr_rel
, RowExclusiveLock
);
7551 return InvalidObjectAddress
;
7554 /* Prevent them from altering a system attribute */
7557 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7558 errmsg("cannot alter system column \"%s\"",
7561 if (attTup
->attidentity
)
7563 (errcode(ERRCODE_SYNTAX_ERROR
),
7564 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7565 colName
, RelationGetRelationName(rel
))));
7568 * It's not OK to remove a constraint only for the parent and leave it in
7569 * the children, so disallow that.
7573 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7575 PartitionDesc partdesc
;
7577 partdesc
= RelationGetPartitionDesc(rel
, true);
7579 if (partdesc
->nparts
> 0)
7581 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7582 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7583 errhint("Do not specify the ONLY keyword."));
7585 else if (rel
->rd_rel
->relhassubclass
&&
7586 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
7589 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7590 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7592 errhint("Do not specify the ONLY keyword."));
7597 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7599 if (rel
->rd_rel
->relispartition
)
7601 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7602 Relation parent
= table_open(parentId
, AccessShareLock
);
7603 TupleDesc tupDesc
= RelationGetDescr(parent
);
7604 AttrNumber parent_attnum
;
7606 parent_attnum
= get_attnum(parentId
, colName
);
7607 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7609 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7610 errmsg("column \"%s\" is marked NOT NULL in parent table",
7612 table_close(parent
, AccessShareLock
);
7616 * Find the constraint that makes this column NOT NULL.
7618 conTup
= findNotNullConstraint(RelationGetRelid(rel
), colName
);
7624 * There's no not-null constraint, so throw an error. If the column
7625 * is in a primary key, we can throw a specific error. Otherwise,
7626 * this is unexpected.
7628 pkcols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_PRIMARY_KEY
);
7629 if (bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
7632 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7633 errmsg("column \"%s\" is in a primary key", colName
));
7635 /* this shouldn't happen */
7636 elog(ERROR
, "could not find not-null constraint on column \"%s\", relation \"%s\"",
7637 colName
, RelationGetRelationName(rel
));
7641 dropconstraint_internal(rel
, conTup
, DROP_RESTRICT
, recurse
, false,
7642 false, &readyRels
, lockmode
);
7644 heap_freetuple(conTup
);
7646 InvokeObjectPostAlterHook(RelationRelationId
,
7647 RelationGetRelid(rel
), attnum
);
7649 table_close(attr_rel
, RowExclusiveLock
);
7655 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7656 * to verify it; recurses to apply the same to children.
7658 * When called to alter an existing table, 'wqueue' must be given so that we can
7659 * queue a check that existing tuples pass the constraint. When called from
7660 * table creation, 'wqueue' should be passed as NULL.
7662 * Returns true if the flag was set in any table, otherwise false.
7665 set_attnotnull(List
**wqueue
, Relation rel
, AttrNumber attnum
, bool recurse
,
7669 Form_pg_attribute attForm
;
7670 bool retval
= false;
7672 /* Guard against stack overflow due to overly deep inheritance tree. */
7673 check_stack_depth();
7675 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
7676 if (!HeapTupleIsValid(tuple
))
7677 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
7678 attnum
, RelationGetRelid(rel
));
7679 attForm
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7680 if (!attForm
->attnotnull
)
7684 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7686 attForm
->attnotnull
= true;
7687 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7689 table_close(attr_rel
, RowExclusiveLock
);
7692 * And set up for existing values to be checked, unless another
7693 * constraint already proves this.
7695 if (wqueue
&& !NotNullImpliedByRelConstraints(rel
, attForm
))
7697 AlteredTableInfo
*tab
;
7699 tab
= ATGetQueueEntry(wqueue
, rel
);
7700 tab
->verify_new_notnull
= true;
7711 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7712 foreach(lc
, children
)
7714 Oid childrelid
= lfirst_oid(lc
);
7716 AttrNumber childattno
;
7718 /* find_inheritance_children already got lock */
7719 childrel
= table_open(childrelid
, NoLock
);
7720 CheckTableNotInUse(childrel
, "ALTER TABLE");
7722 childattno
= get_attnum(RelationGetRelid(childrel
),
7723 get_attname(RelationGetRelid(rel
), attnum
,
7725 retval
|= set_attnotnull(wqueue
, childrel
, childattno
,
7727 table_close(childrel
, NoLock
);
7735 * ALTER TABLE ALTER COLUMN SET NOT NULL
7737 * Add a not-null constraint to a single table and its children. Returns
7738 * the address of the constraint added to the parent relation, if one gets
7739 * added, or InvalidObjectAddress otherwise.
7741 * We must recurse to child tables during execution, rather than using
7742 * ALTER TABLE's normal prep-time recursion.
7744 static ObjectAddress
7745 ATExecSetNotNull(List
**wqueue
, Relation rel
, char *conName
, char *colName
,
7746 bool recurse
, bool recursing
, List
**readyRels
,
7750 Relation constr_rel
;
7752 SysScanDesc conscan
;
7754 ObjectAddress address
;
7755 Constraint
*constraint
;
7756 CookedConstraint
*ccon
;
7758 bool is_no_inherit
= false;
7761 /* Guard against stack overflow due to overly deep inheritance tree. */
7762 check_stack_depth();
7765 * In cases of multiple inheritance, we might visit the same child more
7766 * than once. In the topmost call, set up a list that we fill with all
7767 * visited relations, to skip those.
7769 if (readyRels
== NULL
)
7774 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
7775 return InvalidObjectAddress
;
7776 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
7778 /* At top level, permission check was done in ATPrepCmd, else do it */
7781 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7782 Assert(conName
!= NULL
);
7785 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7786 if (attnum
== InvalidAttrNumber
)
7788 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7789 errmsg("column \"%s\" of relation \"%s\" does not exist",
7790 colName
, RelationGetRelationName(rel
))));
7792 /* Prevent them from altering a system attribute */
7795 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7796 errmsg("cannot alter system column \"%s\"",
7799 /* See if there's already a constraint */
7800 constr_rel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
7802 Anum_pg_constraint_conrelid
,
7803 BTEqualStrategyNumber
, F_OIDEQ
,
7804 ObjectIdGetDatum(RelationGetRelid(rel
)));
7805 conscan
= systable_beginscan(constr_rel
, ConstraintRelidTypidNameIndexId
, true,
7808 while (HeapTupleIsValid(tuple
= systable_getnext(conscan
)))
7810 Form_pg_constraint conForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
7811 bool changed
= false;
7814 if (conForm
->contype
!= CONSTRAINT_NOTNULL
)
7817 if (extractNotNullColumn(tuple
) != attnum
)
7820 copytup
= heap_copytuple(tuple
);
7821 conForm
= (Form_pg_constraint
) GETSTRUCT(copytup
);
7824 * If we find an appropriate constraint, we're almost done, but just
7825 * need to change some properties on it: if we're recursing, increment
7826 * coninhcount; if not, set conislocal if not already set.
7830 conForm
->coninhcount
++;
7833 else if (!conForm
->conislocal
)
7835 conForm
->conislocal
= true;
7841 CatalogTupleUpdate(constr_rel
, ©tup
->t_self
, copytup
);
7842 ObjectAddressSet(address
, ConstraintRelationId
, conForm
->oid
);
7845 systable_endscan(conscan
);
7846 table_close(constr_rel
, RowExclusiveLock
);
7851 return InvalidObjectAddress
;
7854 systable_endscan(conscan
);
7855 table_close(constr_rel
, RowExclusiveLock
);
7858 * If we're asked not to recurse, and children exist, raise an error for
7859 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7863 find_inheritance_children(RelationGetRelid(rel
),
7866 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7868 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7869 errmsg("constraint must be added to child tables too"),
7870 errhint("Do not specify the ONLY keyword."));
7872 is_no_inherit
= true;
7876 * No constraint exists; we must add one. First determine a name to use,
7877 * if we haven't already.
7881 Assert(conName
== NULL
);
7882 conName
= ChooseConstraintName(RelationGetRelationName(rel
),
7883 colName
, "not_null",
7884 RelationGetNamespace(rel
),
7887 constraint
= makeNode(Constraint
);
7888 constraint
->contype
= CONSTR_NOTNULL
;
7889 constraint
->conname
= conName
;
7890 constraint
->deferrable
= false;
7891 constraint
->initdeferred
= false;
7892 constraint
->location
= -1;
7893 constraint
->keys
= list_make1(makeString(colName
));
7894 constraint
->is_no_inherit
= is_no_inherit
;
7895 constraint
->inhcount
= recursing
? 1 : 0;
7896 constraint
->skip_validation
= false;
7897 constraint
->initially_valid
= true;
7900 cooked
= AddRelationNewConstraints(rel
, NIL
, list_make1(constraint
),
7901 false, !recursing
, false, NULL
);
7902 ccon
= linitial(cooked
);
7903 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
7905 InvokeObjectPostAlterHook(RelationRelationId
,
7906 RelationGetRelid(rel
), attnum
);
7909 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7910 * recurse, because we're going to do it here.
7912 set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
);
7915 * Recurse to propagate the constraint to children that don't have one.
7922 children
= find_inheritance_children(RelationGetRelid(rel
),
7925 foreach(lc
, children
)
7929 childrel
= table_open(lfirst_oid(lc
), NoLock
);
7931 ATExecSetNotNull(wqueue
, childrel
,
7932 conName
, colName
, recurse
, true,
7933 readyRels
, lockmode
);
7935 table_close(childrel
, NoLock
);
7943 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
7945 * This doesn't exist in the grammar; it's used when creating a
7946 * primary key and the column is not already marked attnotnull.
7948 static ObjectAddress
7949 ATExecSetAttNotNull(List
**wqueue
, Relation rel
,
7950 const char *colName
, LOCKMODE lockmode
)
7953 ObjectAddress address
= InvalidObjectAddress
;
7955 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7956 if (attnum
== InvalidAttrNumber
)
7958 errcode(ERRCODE_UNDEFINED_COLUMN
),
7959 errmsg("column \"%s\" of relation \"%s\" does not exist",
7960 colName
, RelationGetRelationName(rel
)));
7963 * Make the change, if necessary, and only if so report the column as
7966 if (set_attnotnull(wqueue
, rel
, attnum
, false, lockmode
))
7967 ObjectAddressSubSet(address
, RelationRelationId
,
7968 RelationGetRelid(rel
), attnum
);
7974 * NotNullImpliedByRelConstraints
7975 * Does rel's existing constraints imply NOT NULL for the given attribute?
7978 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
7980 NullTest
*nnulltest
= makeNode(NullTest
);
7982 nnulltest
->arg
= (Expr
*) makeVar(1,
7988 nnulltest
->nulltesttype
= IS_NOT_NULL
;
7991 * argisrow = false is correct even for a composite column, because
7992 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7993 * case, just IS DISTINCT FROM NULL.
7995 nnulltest
->argisrow
= false;
7996 nnulltest
->location
= -1;
7998 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
8001 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8002 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
8010 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8012 * Return the address of the affected column.
8014 static ObjectAddress
8015 ATExecColumnDefault(Relation rel
, const char *colName
,
8016 Node
*newDefault
, LOCKMODE lockmode
)
8018 TupleDesc tupdesc
= RelationGetDescr(rel
);
8020 ObjectAddress address
;
8023 * get the number of the attribute
8025 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
8026 if (attnum
== InvalidAttrNumber
)
8028 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8029 errmsg("column \"%s\" of relation \"%s\" does not exist",
8030 colName
, RelationGetRelationName(rel
))));
8032 /* Prevent them from altering a system attribute */
8035 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8036 errmsg("cannot alter system column \"%s\"",
8039 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
8041 (errcode(ERRCODE_SYNTAX_ERROR
),
8042 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8043 colName
, RelationGetRelationName(rel
)),
8044 /* translator: %s is an SQL ALTER command */
8045 newDefault
? 0 : errhint("Use %s instead.",
8046 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8048 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
8050 (errcode(ERRCODE_SYNTAX_ERROR
),
8051 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8052 colName
, RelationGetRelationName(rel
)),
8054 /* translator: %s is an SQL ALTER command */
8055 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8056 (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
== ATTRIBUTE_GENERATED_STORED
?
8057 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8060 * Remove any old default for the column. We use RESTRICT here for
8061 * safety, but at present we do not expect anything to depend on the
8064 * We treat removing the existing default as an internal operation when it
8065 * is preparatory to adding a new default, but as a user-initiated
8066 * operation when the user asked for a drop.
8068 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8069 newDefault
!= NULL
);
8074 RawColumnDefault
*rawEnt
;
8076 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8077 rawEnt
->attnum
= attnum
;
8078 rawEnt
->raw_default
= newDefault
;
8079 rawEnt
->missingMode
= false;
8080 rawEnt
->generated
= '\0';
8083 * This function is intended for CREATE TABLE, so it processes a
8084 * _list_ of defaults, but we just do one.
8086 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8087 false, true, false, NULL
);
8090 ObjectAddressSubSet(address
, RelationRelationId
,
8091 RelationGetRelid(rel
), attnum
);
8096 * Add a pre-cooked default expression.
8098 * Return the address of the affected column.
8100 static ObjectAddress
8101 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
8104 ObjectAddress address
;
8106 /* We assume no checking is required */
8109 * Remove any old default for the column. We use RESTRICT here for
8110 * safety, but at present we do not expect anything to depend on the
8111 * default. (In ordinary cases, there could not be a default in place
8112 * anyway, but it's possible when combining LIKE with inheritance.)
8114 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
8117 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
8119 ObjectAddressSubSet(address
, RelationRelationId
,
8120 RelationGetRelid(rel
), attnum
);
8125 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8127 * Return the address of the affected column.
8129 static ObjectAddress
8130 ATExecAddIdentity(Relation rel
, const char *colName
,
8131 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
)
8133 Relation attrelation
;
8135 Form_pg_attribute attTup
;
8137 ObjectAddress address
;
8138 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
8141 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8142 if (ispartitioned
&& !recurse
)
8144 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8145 errmsg("cannot add identity to a column of only the partitioned table"),
8146 errhint("Do not specify the ONLY keyword.")));
8148 if (rel
->rd_rel
->relispartition
&& !recursing
)
8150 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8151 errmsg("cannot add identity to a column of a partition"));
8153 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8155 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8156 if (!HeapTupleIsValid(tuple
))
8158 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8159 errmsg("column \"%s\" of relation \"%s\" does not exist",
8160 colName
, RelationGetRelationName(rel
))));
8161 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8162 attnum
= attTup
->attnum
;
8164 /* Can't alter a system attribute */
8167 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8168 errmsg("cannot alter system column \"%s\"",
8172 * Creating a column as identity implies NOT NULL, so adding the identity
8173 * to an existing column that is not NOT NULL would create a state that
8174 * cannot be reproduced without contortions.
8176 if (!attTup
->attnotnull
)
8178 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8179 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8180 colName
, RelationGetRelationName(rel
))));
8182 if (attTup
->attidentity
)
8184 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8185 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8186 colName
, RelationGetRelationName(rel
))));
8188 if (attTup
->atthasdef
)
8190 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8191 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8192 colName
, RelationGetRelationName(rel
))));
8194 attTup
->attidentity
= cdef
->identity
;
8195 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8197 InvokeObjectPostAlterHook(RelationRelationId
,
8198 RelationGetRelid(rel
),
8200 ObjectAddressSubSet(address
, RelationRelationId
,
8201 RelationGetRelid(rel
), attnum
);
8202 heap_freetuple(tuple
);
8204 table_close(attrelation
, RowExclusiveLock
);
8207 * Recurse to propagate the identity column to partitions. Identity is
8208 * not inherited in regular inheritance children.
8210 if (recurse
&& ispartitioned
)
8215 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8217 foreach(lc
, children
)
8221 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8222 ATExecAddIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8223 table_close(childrel
, NoLock
);
8231 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8233 * Return the address of the affected column.
8235 static ObjectAddress
8236 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
,
8237 LOCKMODE lockmode
, bool recurse
, bool recursing
)
8240 DefElem
*generatedEl
= NULL
;
8242 Form_pg_attribute attTup
;
8244 Relation attrelation
;
8245 ObjectAddress address
;
8248 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8249 if (ispartitioned
&& !recurse
)
8251 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8252 errmsg("cannot change identity column of only the partitioned table"),
8253 errhint("Do not specify the ONLY keyword.")));
8255 if (rel
->rd_rel
->relispartition
&& !recursing
)
8257 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8258 errmsg("cannot change identity column of a partition"));
8260 foreach(option
, castNode(List
, def
))
8262 DefElem
*defel
= lfirst_node(DefElem
, option
);
8264 if (strcmp(defel
->defname
, "generated") == 0)
8268 (errcode(ERRCODE_SYNTAX_ERROR
),
8269 errmsg("conflicting or redundant options")));
8270 generatedEl
= defel
;
8273 elog(ERROR
, "option \"%s\" not recognized",
8278 * Even if there is nothing to change here, we run all the checks. There
8279 * will be a subsequent ALTER SEQUENCE that relies on everything being
8283 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8284 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8285 if (!HeapTupleIsValid(tuple
))
8287 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8288 errmsg("column \"%s\" of relation \"%s\" does not exist",
8289 colName
, RelationGetRelationName(rel
))));
8291 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8292 attnum
= attTup
->attnum
;
8296 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8297 errmsg("cannot alter system column \"%s\"",
8300 if (!attTup
->attidentity
)
8302 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8303 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8304 colName
, RelationGetRelationName(rel
))));
8308 attTup
->attidentity
= defGetInt32(generatedEl
);
8309 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8311 InvokeObjectPostAlterHook(RelationRelationId
,
8312 RelationGetRelid(rel
),
8314 ObjectAddressSubSet(address
, RelationRelationId
,
8315 RelationGetRelid(rel
), attnum
);
8318 address
= InvalidObjectAddress
;
8320 heap_freetuple(tuple
);
8321 table_close(attrelation
, RowExclusiveLock
);
8324 * Recurse to propagate the identity change to partitions. Identity is not
8325 * inherited in regular inheritance children.
8327 if (generatedEl
&& recurse
&& ispartitioned
)
8332 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8334 foreach(lc
, children
)
8338 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8339 ATExecSetIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8340 table_close(childrel
, NoLock
);
8348 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8350 * Return the address of the affected column.
8352 static ObjectAddress
8353 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
8354 bool recurse
, bool recursing
)
8357 Form_pg_attribute attTup
;
8359 Relation attrelation
;
8360 ObjectAddress address
;
8362 ObjectAddress seqaddress
;
8365 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8366 if (ispartitioned
&& !recurse
)
8368 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8369 errmsg("cannot drop identity from a column of only the partitioned table"),
8370 errhint("Do not specify the ONLY keyword.")));
8372 if (rel
->rd_rel
->relispartition
&& !recursing
)
8374 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8375 errmsg("cannot drop identity from a column of a partition"));
8377 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8378 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8379 if (!HeapTupleIsValid(tuple
))
8381 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8382 errmsg("column \"%s\" of relation \"%s\" does not exist",
8383 colName
, RelationGetRelationName(rel
))));
8385 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8386 attnum
= attTup
->attnum
;
8390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8391 errmsg("cannot alter system column \"%s\"",
8394 if (!attTup
->attidentity
)
8398 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8399 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8400 colName
, RelationGetRelationName(rel
))));
8404 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8405 colName
, RelationGetRelationName(rel
))));
8406 heap_freetuple(tuple
);
8407 table_close(attrelation
, RowExclusiveLock
);
8408 return InvalidObjectAddress
;
8412 attTup
->attidentity
= '\0';
8413 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8415 InvokeObjectPostAlterHook(RelationRelationId
,
8416 RelationGetRelid(rel
),
8418 ObjectAddressSubSet(address
, RelationRelationId
,
8419 RelationGetRelid(rel
), attnum
);
8420 heap_freetuple(tuple
);
8422 table_close(attrelation
, RowExclusiveLock
);
8425 * Recurse to drop the identity from column in partitions. Identity is
8426 * not inherited in regular inheritance children so ignore them.
8428 if (recurse
&& ispartitioned
)
8433 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8435 foreach(lc
, children
)
8439 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8440 ATExecDropIdentity(childrel
, colName
, false, lockmode
, recurse
, true);
8441 table_close(childrel
, NoLock
);
8447 /* drop the internal sequence */
8448 seqid
= getIdentitySequence(RelationGetRelid(rel
), attnum
, false);
8449 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
8450 RelationRelationId
, DEPENDENCY_INTERNAL
);
8451 CommandCounterIncrement();
8452 seqaddress
.classId
= RelationRelationId
;
8453 seqaddress
.objectId
= seqid
;
8454 seqaddress
.objectSubId
= 0;
8455 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8462 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8464 * Return the address of the affected column.
8466 static ObjectAddress
8467 ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
8468 Node
*newExpr
, LOCKMODE lockmode
)
8471 Form_pg_attribute attTup
;
8474 ObjectAddress address
;
8476 NewColumnValue
*newval
;
8477 RawColumnDefault
*rawEnt
;
8479 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8480 if (!HeapTupleIsValid(tuple
))
8482 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8483 errmsg("column \"%s\" of relation \"%s\" does not exist",
8484 colName
, RelationGetRelationName(rel
))));
8486 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8487 attnum
= attTup
->attnum
;
8491 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8492 errmsg("cannot alter system column \"%s\"",
8495 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8497 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8498 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8499 colName
, RelationGetRelationName(rel
))));
8500 ReleaseSysCache(tuple
);
8503 * Clear all the missing values if we're rewriting the table, since this
8504 * renders them pointless.
8506 RelationClearMissing(rel
);
8508 /* make sure we don't conflict with later attribute modifications */
8509 CommandCounterIncrement();
8512 * Find everything that depends on the column (constraints, indexes, etc),
8513 * and record enough information to let us recreate the objects after
8516 RememberAllDependentForRebuilding(tab
, AT_SetExpression
, rel
, attnum
, colName
);
8519 * Drop the dependency records of the GENERATED expression, in particular
8520 * its INTERNAL dependency on the column, which would otherwise cause
8521 * dependency.c to refuse to perform the deletion.
8523 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8524 if (!OidIsValid(attrdefoid
))
8525 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8526 RelationGetRelid(rel
), attnum
);
8527 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8529 /* Make above changes visible */
8530 CommandCounterIncrement();
8533 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8534 * safety, but at present we do not expect anything to depend on the
8537 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8540 /* Prepare to store the new expression, in the catalogs */
8541 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8542 rawEnt
->attnum
= attnum
;
8543 rawEnt
->raw_default
= newExpr
;
8544 rawEnt
->missingMode
= false;
8545 rawEnt
->generated
= ATTRIBUTE_GENERATED_STORED
;
8547 /* Store the generated expression */
8548 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8549 false, true, false, NULL
);
8551 /* Make above new expression visible */
8552 CommandCounterIncrement();
8554 /* Prepare for table rewrite */
8555 defval
= (Expr
*) build_column_default(rel
, attnum
);
8557 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
8558 newval
->attnum
= attnum
;
8559 newval
->expr
= expression_planner(defval
);
8560 newval
->is_generated
= true;
8562 tab
->newvals
= lappend(tab
->newvals
, newval
);
8563 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
8565 /* Drop any pg_statistic entry for the column */
8566 RemoveStatistics(RelationGetRelid(rel
), attnum
);
8568 InvokeObjectPostAlterHook(RelationRelationId
,
8569 RelationGetRelid(rel
), attnum
);
8571 ObjectAddressSubSet(address
, RelationRelationId
,
8572 RelationGetRelid(rel
), attnum
);
8577 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8580 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8583 * Reject ONLY if there are child tables. We could implement this, but it
8584 * is a bit complicated. GENERATED clauses must be attached to the column
8585 * definition and cannot be added later like DEFAULT, so if a child table
8586 * has a generation expression that the parent does not have, the child
8587 * column will necessarily be an attislocal column. So to implement ONLY
8588 * here, we'd need extra code to update attislocal of the direct child
8589 * tables, somewhat similar to how DROP COLUMN does it, so that the
8590 * resulting state can be properly dumped and restored.
8593 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8595 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8596 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8599 * Cannot drop generation expression from inherited columns.
8604 Form_pg_attribute attTup
;
8606 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8607 if (!HeapTupleIsValid(tuple
))
8609 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8610 errmsg("column \"%s\" of relation \"%s\" does not exist",
8611 cmd
->name
, RelationGetRelationName(rel
))));
8613 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8615 if (attTup
->attinhcount
> 0)
8617 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8618 errmsg("cannot drop generation expression from inherited column")));
8623 * Return the address of the affected column.
8625 static ObjectAddress
8626 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8629 Form_pg_attribute attTup
;
8631 Relation attrelation
;
8633 ObjectAddress address
;
8635 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8636 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8637 if (!HeapTupleIsValid(tuple
))
8639 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8640 errmsg("column \"%s\" of relation \"%s\" does not exist",
8641 colName
, RelationGetRelationName(rel
))));
8643 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8644 attnum
= attTup
->attnum
;
8648 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8649 errmsg("cannot alter system column \"%s\"",
8652 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8656 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8657 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8658 colName
, RelationGetRelationName(rel
))));
8662 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8663 colName
, RelationGetRelationName(rel
))));
8664 heap_freetuple(tuple
);
8665 table_close(attrelation
, RowExclusiveLock
);
8666 return InvalidObjectAddress
;
8671 * Mark the column as no longer generated. (The atthasdef flag needs to
8672 * get cleared too, but RemoveAttrDefault will handle that.)
8674 attTup
->attgenerated
= '\0';
8675 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8677 InvokeObjectPostAlterHook(RelationRelationId
,
8678 RelationGetRelid(rel
),
8680 heap_freetuple(tuple
);
8682 table_close(attrelation
, RowExclusiveLock
);
8685 * Drop the dependency records of the GENERATED expression, in particular
8686 * its INTERNAL dependency on the column, which would otherwise cause
8687 * dependency.c to refuse to perform the deletion.
8689 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8690 if (!OidIsValid(attrdefoid
))
8691 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8692 RelationGetRelid(rel
), attnum
);
8693 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8695 /* Make above changes visible */
8696 CommandCounterIncrement();
8699 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8700 * safety, but at present we do not expect anything to depend on the
8703 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8706 ObjectAddressSubSet(address
, RelationRelationId
,
8707 RelationGetRelid(rel
), attnum
);
8712 * ALTER TABLE ALTER COLUMN SET STATISTICS
8714 * Return value is the address of the modified column
8716 static ObjectAddress
8717 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8720 bool newtarget_default
;
8721 Relation attrelation
;
8724 Form_pg_attribute attrtuple
;
8726 ObjectAddress address
;
8727 Datum repl_val
[Natts_pg_attribute
];
8728 bool repl_null
[Natts_pg_attribute
];
8729 bool repl_repl
[Natts_pg_attribute
];
8732 * We allow referencing columns by numbers only for indexes, since table
8733 * column numbers could contain gaps if columns are later dropped.
8735 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8736 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8739 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8740 errmsg("cannot refer to non-index column by number")));
8742 /* -1 was used in previous versions for the default setting */
8743 if (newValue
&& intVal(newValue
) != -1)
8745 newtarget
= intVal(newValue
);
8746 newtarget_default
= false;
8749 newtarget_default
= true;
8751 if (!newtarget_default
)
8754 * Limit target to a sane range
8759 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8760 errmsg("statistics target %d is too low",
8763 else if (newtarget
> MAX_STATISTICS_TARGET
)
8765 newtarget
= MAX_STATISTICS_TARGET
;
8767 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8768 errmsg("lowering statistics target to %d",
8773 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8777 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8779 if (!HeapTupleIsValid(tuple
))
8781 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8782 errmsg("column \"%s\" of relation \"%s\" does not exist",
8783 colName
, RelationGetRelationName(rel
))));
8787 tuple
= SearchSysCacheAttNum(RelationGetRelid(rel
), colNum
);
8789 if (!HeapTupleIsValid(tuple
))
8791 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8792 errmsg("column number %d of relation \"%s\" does not exist",
8793 colNum
, RelationGetRelationName(rel
))));
8796 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8798 attnum
= attrtuple
->attnum
;
8801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8802 errmsg("cannot alter system column \"%s\"",
8805 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8806 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8808 if (attnum
> rel
->rd_index
->indnkeyatts
)
8810 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8811 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8812 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8813 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8816 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8817 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8818 errhint("Alter statistics on table column instead.")));
8821 /* Build new tuple. */
8822 memset(repl_null
, false, sizeof(repl_null
));
8823 memset(repl_repl
, false, sizeof(repl_repl
));
8824 if (!newtarget_default
)
8825 repl_val
[Anum_pg_attribute_attstattarget
- 1] = newtarget
;
8827 repl_null
[Anum_pg_attribute_attstattarget
- 1] = true;
8828 repl_repl
[Anum_pg_attribute_attstattarget
- 1] = true;
8829 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8830 repl_val
, repl_null
, repl_repl
);
8831 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, newtuple
);
8833 InvokeObjectPostAlterHook(RelationRelationId
,
8834 RelationGetRelid(rel
),
8836 ObjectAddressSubSet(address
, RelationRelationId
,
8837 RelationGetRelid(rel
), attnum
);
8839 heap_freetuple(newtuple
);
8841 ReleaseSysCache(tuple
);
8843 table_close(attrelation
, RowExclusiveLock
);
8849 * Return value is the address of the modified column
8851 static ObjectAddress
8852 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8853 bool isReset
, LOCKMODE lockmode
)
8855 Relation attrelation
;
8858 Form_pg_attribute attrtuple
;
8863 ObjectAddress address
;
8864 Datum repl_val
[Natts_pg_attribute
];
8865 bool repl_null
[Natts_pg_attribute
];
8866 bool repl_repl
[Natts_pg_attribute
];
8868 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8870 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8872 if (!HeapTupleIsValid(tuple
))
8874 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8875 errmsg("column \"%s\" of relation \"%s\" does not exist",
8876 colName
, RelationGetRelationName(rel
))));
8877 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8879 attnum
= attrtuple
->attnum
;
8882 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8883 errmsg("cannot alter system column \"%s\"",
8886 /* Generate new proposed attoptions (text array) */
8887 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8889 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8890 castNode(List
, options
), NULL
, NULL
,
8892 /* Validate new options */
8893 (void) attribute_reloptions(newOptions
, true);
8895 /* Build new tuple. */
8896 memset(repl_null
, false, sizeof(repl_null
));
8897 memset(repl_repl
, false, sizeof(repl_repl
));
8898 if (newOptions
!= (Datum
) 0)
8899 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8901 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8902 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8903 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8904 repl_val
, repl_null
, repl_repl
);
8906 /* Update system catalog. */
8907 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8909 InvokeObjectPostAlterHook(RelationRelationId
,
8910 RelationGetRelid(rel
),
8912 ObjectAddressSubSet(address
, RelationRelationId
,
8913 RelationGetRelid(rel
), attnum
);
8915 heap_freetuple(newtuple
);
8917 ReleaseSysCache(tuple
);
8919 table_close(attrelation
, RowExclusiveLock
);
8925 * Helper function for ATExecSetStorage and ATExecSetCompression
8927 * Set the attstorage and/or attcompression fields for index columns
8928 * associated with the specified table column.
8931 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
8933 bool setstorage
, char newstorage
,
8934 bool setcompression
, char newcompression
,
8939 foreach(lc
, RelationGetIndexList(rel
))
8941 Oid indexoid
= lfirst_oid(lc
);
8943 AttrNumber indattnum
= 0;
8946 indrel
= index_open(indexoid
, lockmode
);
8948 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
8950 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
8959 index_close(indrel
, lockmode
);
8963 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
8965 if (HeapTupleIsValid(tuple
))
8967 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8970 attrtuple
->attstorage
= newstorage
;
8973 attrtuple
->attcompression
= newcompression
;
8975 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8977 InvokeObjectPostAlterHook(RelationRelationId
,
8978 RelationGetRelid(rel
),
8981 heap_freetuple(tuple
);
8984 index_close(indrel
, lockmode
);
8989 * ALTER TABLE ALTER COLUMN SET STORAGE
8991 * Return value is the address of the modified column
8993 static ObjectAddress
8994 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
8996 Relation attrelation
;
8998 Form_pg_attribute attrtuple
;
9000 ObjectAddress address
;
9002 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
9004 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
9006 if (!HeapTupleIsValid(tuple
))
9008 (errcode(ERRCODE_UNDEFINED_COLUMN
),
9009 errmsg("column \"%s\" of relation \"%s\" does not exist",
9010 colName
, RelationGetRelationName(rel
))));
9011 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9013 attnum
= attrtuple
->attnum
;
9016 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9017 errmsg("cannot alter system column \"%s\"",
9020 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
9022 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
9024 InvokeObjectPostAlterHook(RelationRelationId
,
9025 RelationGetRelid(rel
),
9029 * Apply the change to indexes as well (only for simple index columns,
9030 * matching behavior of index.c ConstructTupleDescriptor()).
9032 SetIndexStorageProperties(rel
, attrelation
, attnum
,
9033 true, attrtuple
->attstorage
,
9037 heap_freetuple(tuple
);
9039 table_close(attrelation
, RowExclusiveLock
);
9041 ObjectAddressSubSet(address
, RelationRelationId
,
9042 RelationGetRelid(rel
), attnum
);
9048 * ALTER TABLE DROP COLUMN
9050 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9051 * because we have to decide at runtime whether to recurse or not depending
9052 * on whether attinhcount goes to zero or not. (We can't check this in a
9053 * static pre-pass because it won't handle multiple inheritance situations
9057 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
9058 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
9059 AlterTableUtilityContext
*context
)
9061 if (rel
->rd_rel
->reloftype
&& !recursing
)
9063 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9064 errmsg("cannot drop column from typed table")));
9066 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
9067 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
9070 cmd
->recurse
= true;
9074 * Drops column 'colName' from relation 'rel' and returns the address of the
9075 * dropped column. The column is also dropped (or marked as no longer
9076 * inherited from relation) from the relation's inheritance children, if any.
9078 * In the recursive invocations for inheritance child relations, instead of
9079 * dropping the column directly (if to be dropped at all), its object address
9080 * is added to 'addrs', which must be non-NULL in such invocations. All
9081 * columns are dropped at the same time after all the children have been
9082 * checked recursively.
9084 static ObjectAddress
9085 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
9086 DropBehavior behavior
,
9087 bool recurse
, bool recursing
,
9088 bool missing_ok
, LOCKMODE lockmode
,
9089 ObjectAddresses
*addrs
)
9092 Form_pg_attribute targetatt
;
9095 ObjectAddress object
;
9098 /* At top level, permission check was done in ATPrepCmd, else do it */
9100 ATSimplePermissions(AT_DropColumn
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9102 /* Initialize addrs on the first invocation */
9103 Assert(!recursing
|| addrs
!= NULL
);
9105 /* since this function recurses, it could be driven to stack overflow */
9106 check_stack_depth();
9109 addrs
= new_object_addresses();
9112 * get the number of the attribute
9114 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
9115 if (!HeapTupleIsValid(tuple
))
9120 (errcode(ERRCODE_UNDEFINED_COLUMN
),
9121 errmsg("column \"%s\" of relation \"%s\" does not exist",
9122 colName
, RelationGetRelationName(rel
))));
9127 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9128 colName
, RelationGetRelationName(rel
))));
9129 return InvalidObjectAddress
;
9132 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9134 attnum
= targetatt
->attnum
;
9136 /* Can't drop a system attribute */
9139 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9140 errmsg("cannot drop system column \"%s\"",
9144 * Don't drop inherited columns, unless recursing (presumably from a drop
9145 * of the parent column)
9147 if (targetatt
->attinhcount
> 0 && !recursing
)
9149 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9150 errmsg("cannot drop inherited column \"%s\"",
9154 * Don't drop columns used in the partition key, either. (If we let this
9155 * go through, the key column's dependencies would cause a cascaded drop
9156 * of the whole table, which is surely not what the user expected.)
9158 if (has_partition_attrs(rel
,
9159 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
9162 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9163 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9164 colName
, RelationGetRelationName(rel
))));
9166 ReleaseSysCache(tuple
);
9169 * Propagate to children as appropriate. Unlike most other ALTER
9170 * routines, we have to do this one level of recursion at a time; we can't
9171 * use find_all_inheritors to do it in one pass.
9174 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9182 * In case of a partitioned table, the column must be dropped from the
9183 * partitions as well.
9185 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
9187 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9188 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9189 errhint("Do not specify the ONLY keyword.")));
9191 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
9192 foreach(child
, children
)
9194 Oid childrelid
= lfirst_oid(child
);
9196 Form_pg_attribute childatt
;
9198 /* find_inheritance_children already got lock */
9199 childrel
= table_open(childrelid
, NoLock
);
9200 CheckTableNotInUse(childrel
, "ALTER TABLE");
9202 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
9203 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
9204 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
9205 colName
, childrelid
);
9206 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9208 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
9209 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
9210 childrelid
, colName
);
9215 * If the child column has other definition sources, just
9216 * decrement its inheritance count; if not, recurse to delete
9219 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
9221 /* Time to delete this child column, too */
9222 ATExecDropColumn(wqueue
, childrel
, colName
,
9223 behavior
, true, true,
9224 false, lockmode
, addrs
);
9228 /* Child column must survive my deletion */
9229 childatt
->attinhcount
--;
9231 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9233 /* Make update visible */
9234 CommandCounterIncrement();
9240 * If we were told to drop ONLY in this table (no recursion),
9241 * we need to mark the inheritors' attributes as locally
9242 * defined rather than inherited.
9244 childatt
->attinhcount
--;
9245 childatt
->attislocal
= true;
9247 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9249 /* Make update visible */
9250 CommandCounterIncrement();
9253 heap_freetuple(tuple
);
9255 table_close(childrel
, NoLock
);
9257 table_close(attr_rel
, RowExclusiveLock
);
9260 /* Add object to delete */
9261 object
.classId
= RelationRelationId
;
9262 object
.objectId
= RelationGetRelid(rel
);
9263 object
.objectSubId
= attnum
;
9264 add_exact_object_address(&object
, addrs
);
9268 /* Recursion has ended, drop everything that was collected */
9269 performMultipleDeletions(addrs
, behavior
, 0);
9270 free_object_addresses(addrs
);
9277 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9278 * constraint on its children.
9281 ATPrepAddPrimaryKey(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
9282 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
9285 List
*newconstrs
= NIL
;
9287 IndexStmt
*indexstmt
;
9289 /* No work if not creating a primary key */
9290 if (!IsA(cmd
->def
, IndexStmt
))
9292 indexstmt
= castNode(IndexStmt
, cmd
->def
);
9293 if (!indexstmt
->primary
)
9296 /* No work if no legacy inheritance children are present */
9297 if (rel
->rd_rel
->relkind
!= RELKIND_RELATION
||
9298 !rel
->rd_rel
->relhassubclass
)
9301 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9303 foreach(lc
, indexstmt
->indexParams
)
9305 IndexElem
*elem
= lfirst_node(IndexElem
, lc
);
9306 Constraint
*nnconstr
;
9308 Assert(elem
->expr
== NULL
);
9310 nnconstr
= makeNode(Constraint
);
9311 nnconstr
->contype
= CONSTR_NOTNULL
;
9312 nnconstr
->conname
= NULL
; /* XXX use PK name? */
9313 nnconstr
->inhcount
= 1;
9314 nnconstr
->deferrable
= false;
9315 nnconstr
->initdeferred
= false;
9316 nnconstr
->location
= -1;
9317 nnconstr
->keys
= list_make1(makeString(elem
->name
));
9318 nnconstr
->skip_validation
= false;
9319 nnconstr
->initially_valid
= true;
9321 newconstrs
= lappend(newconstrs
, nnconstr
);
9324 foreach(lc
, children
)
9326 Oid childrelid
= lfirst_oid(lc
);
9327 Relation childrel
= table_open(childrelid
, NoLock
);
9328 AlterTableCmd
*newcmd
= makeNode(AlterTableCmd
);
9331 newcmd
->subtype
= AT_AddConstraint
;
9332 newcmd
->recurse
= true;
9334 foreach(lc2
, newconstrs
)
9336 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9337 newcmd
->def
= lfirst(lc2
);
9339 ATPrepCmd(wqueue
, childrel
, newcmd
,
9340 true, false, lockmode
, context
);
9343 table_close(childrel
, NoLock
);
9348 * ALTER TABLE ADD INDEX
9350 * There is no such command in the grammar, but parse_utilcmd.c converts
9351 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9352 * us schedule creation of the index at the appropriate time during ALTER.
9354 * Return value is the address of the new index.
9356 static ObjectAddress
9357 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
9358 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9363 ObjectAddress address
;
9365 Assert(IsA(stmt
, IndexStmt
));
9366 Assert(!stmt
->concurrent
);
9368 /* The IndexStmt has already been through transformIndexStmt */
9369 Assert(stmt
->transformed
);
9371 /* suppress schema rights check when rebuilding existing index */
9372 check_rights
= !is_rebuild
;
9373 /* skip index build if phase 3 will do it or we're reusing an old one */
9374 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
9375 /* suppress notices when rebuilding existing index */
9378 address
= DefineIndex(RelationGetRelid(rel
),
9380 InvalidOid
, /* no predefined OID */
9381 InvalidOid
, /* no parent index */
9382 InvalidOid
, /* no parent constraint */
9383 -1, /* total_parts unknown */
9384 true, /* is_alter_table */
9386 false, /* check_not_in_use - we did it already */
9391 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9392 * new index instead of building from scratch. Restore associated fields.
9393 * This may store InvalidSubTransactionId in both fields, in which case
9394 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9395 * this after the CCI that made catalog rows visible to any rebuild. The
9396 * DROP of the old edition of this index will have scheduled the storage
9397 * for deletion at commit, so cancel that pending deletion.
9399 if (RelFileNumberIsValid(stmt
->oldNumber
))
9401 Relation irel
= index_open(address
.objectId
, NoLock
);
9403 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
9404 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
9405 RelationPreserveStorage(irel
->rd_locator
, true);
9406 index_close(irel
, NoLock
);
9413 * ALTER TABLE ADD STATISTICS
9415 * This is no such command in the grammar, but we use this internally to add
9416 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9417 * column type change.
9419 static ObjectAddress
9420 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
9421 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9423 ObjectAddress address
;
9425 Assert(IsA(stmt
, CreateStatsStmt
));
9427 /* The CreateStatsStmt has already been through transformStatsStmt */
9428 Assert(stmt
->transformed
);
9430 address
= CreateStatistics(stmt
);
9436 * ALTER TABLE ADD CONSTRAINT USING INDEX
9438 * Returns the address of the new constraint.
9440 static ObjectAddress
9441 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
9442 IndexStmt
*stmt
, LOCKMODE lockmode
)
9444 Oid index_oid
= stmt
->indexOid
;
9447 IndexInfo
*indexInfo
;
9448 char *constraintName
;
9449 char constraintType
;
9450 ObjectAddress address
;
9453 Assert(IsA(stmt
, IndexStmt
));
9454 Assert(OidIsValid(index_oid
));
9455 Assert(stmt
->isconstraint
);
9458 * Doing this on partitioned tables is not a simple feature to implement,
9459 * so let's punt for now.
9461 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9463 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9464 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9466 indexRel
= index_open(index_oid
, AccessShareLock
);
9468 indexName
= pstrdup(RelationGetRelationName(indexRel
));
9470 indexInfo
= BuildIndexInfo(indexRel
);
9472 /* this should have been checked at parse time */
9473 if (!indexInfo
->ii_Unique
)
9474 elog(ERROR
, "index \"%s\" is not unique", indexName
);
9477 * Determine name to assign to constraint. We require a constraint to
9478 * have the same name as the underlying index; therefore, use the index's
9479 * existing name as the default constraint name, and if the user
9480 * explicitly gives some other name for the constraint, rename the index
9483 constraintName
= stmt
->idxname
;
9484 if (constraintName
== NULL
)
9485 constraintName
= indexName
;
9486 else if (strcmp(constraintName
, indexName
) != 0)
9489 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9490 indexName
, constraintName
)));
9491 RenameRelationInternal(index_oid
, constraintName
, false, true);
9494 /* Extra checks needed if making primary key */
9496 index_check_primary_key(rel
, indexInfo
, true, stmt
);
9498 /* Note we currently don't support EXCLUSION constraints here */
9500 constraintType
= CONSTRAINT_PRIMARY
;
9502 constraintType
= CONSTRAINT_UNIQUE
;
9504 /* Create the catalog entries for the constraint */
9505 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
9506 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
9507 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
9508 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
9509 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
9511 address
= index_constraint_create(rel
,
9518 allowSystemTableMods
,
9519 false); /* is_internal */
9521 index_close(indexRel
, NoLock
);
9527 * ALTER TABLE ADD CONSTRAINT
9529 * Return value is the address of the new constraint; if no constraint was
9530 * added, InvalidObjectAddress is returned.
9532 static ObjectAddress
9533 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9534 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
9537 ObjectAddress address
= InvalidObjectAddress
;
9539 Assert(IsA(newConstraint
, Constraint
));
9542 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9543 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9546 switch (newConstraint
->contype
)
9549 case CONSTR_NOTNULL
:
9551 ATAddCheckNNConstraint(wqueue
, tab
, rel
,
9552 newConstraint
, recurse
, false, is_readd
,
9556 case CONSTR_FOREIGN
:
9559 * Assign or validate constraint name
9561 if (newConstraint
->conname
)
9563 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9564 RelationGetRelid(rel
),
9565 newConstraint
->conname
))
9567 (errcode(ERRCODE_DUPLICATE_OBJECT
),
9568 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9569 newConstraint
->conname
,
9570 RelationGetRelationName(rel
))));
9573 newConstraint
->conname
=
9574 ChooseConstraintName(RelationGetRelationName(rel
),
9575 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
9577 RelationGetNamespace(rel
),
9580 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
9587 elog(ERROR
, "unrecognized constraint type: %d",
9588 (int) newConstraint
->contype
);
9595 * Generate the column-name portion of the constraint name for a new foreign
9596 * key given the list of column names that reference the referenced
9597 * table. This will be passed to ChooseConstraintName along with the parent
9598 * table name and the "fkey" suffix.
9600 * We know that less than NAMEDATALEN characters will actually be used, so we
9601 * can truncate the result once we've generated that many.
9603 * XXX see also ChooseExtendedStatisticNameAddition and
9604 * ChooseIndexNameAddition.
9607 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
9609 char buf
[NAMEDATALEN
* 2];
9614 foreach(lc
, colnames
)
9616 const char *name
= strVal(lfirst(lc
));
9619 buf
[buflen
++] = '_'; /* insert _ between names */
9622 * At this point we have buflen <= NAMEDATALEN. name should be less
9623 * than NAMEDATALEN already, but use strlcpy for paranoia.
9625 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
9626 buflen
+= strlen(buf
+ buflen
);
9627 if (buflen
>= NAMEDATALEN
)
9630 return pstrdup(buf
);
9634 * Add a check or not-null constraint to a single table and its children.
9635 * Returns the address of the constraint added to the parent relation,
9636 * if one gets added, or InvalidObjectAddress otherwise.
9638 * Subroutine for ATExecAddConstraint.
9640 * We must recurse to child tables during execution, rather than using
9641 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9642 * constraints *must* be given the same name, else they won't be seen as
9643 * related later. If the user didn't explicitly specify a name, then
9644 * AddRelationNewConstraints would normally assign different names to the
9645 * child constraints. To fix that, we must capture the name assigned at
9646 * the parent table and pass that down.
9648 static ObjectAddress
9649 ATAddCheckNNConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9650 Constraint
*constr
, bool recurse
, bool recursing
,
9651 bool is_readd
, LOCKMODE lockmode
)
9657 ObjectAddress address
= InvalidObjectAddress
;
9659 /* Guard against stack overflow due to overly deep inheritance tree. */
9660 check_stack_depth();
9662 /* At top level, permission check was done in ATPrepCmd, else do it */
9664 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9667 * Call AddRelationNewConstraints to do the work, making sure it works on
9668 * a copy of the Constraint so transformExpr can't modify the original. It
9669 * returns a list of cooked constraints.
9671 * If the constraint ends up getting merged with a pre-existing one, it's
9672 * omitted from the returned list, which is what we want: we do not need
9673 * to do any validation work. That can only happen at child tables,
9674 * though, since we disallow merging at the top level.
9676 newcons
= AddRelationNewConstraints(rel
, NIL
,
9677 list_make1(copyObject(constr
)),
9678 recursing
|| is_readd
, /* allow_merge */
9679 !recursing
, /* is_local */
9680 is_readd
, /* is_internal */
9681 NULL
); /* queryString not available
9684 /* we don't expect more than one constraint here */
9685 Assert(list_length(newcons
) <= 1);
9687 /* Add each to-be-validated constraint to Phase 3's queue */
9688 foreach(lcon
, newcons
)
9690 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9692 if (!ccon
->skip_validation
&& ccon
->contype
!= CONSTR_NOTNULL
)
9694 NewConstraint
*newcon
;
9696 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9697 newcon
->name
= ccon
->name
;
9698 newcon
->contype
= ccon
->contype
;
9699 newcon
->qual
= ccon
->expr
;
9701 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9704 /* Save the actually assigned name if it was defaulted */
9705 if (constr
->conname
== NULL
)
9706 constr
->conname
= ccon
->name
;
9709 * If adding a not-null constraint, set the pg_attribute flag and tell
9710 * phase 3 to verify existing rows, if needed.
9712 if (constr
->contype
== CONSTR_NOTNULL
)
9713 set_attnotnull(wqueue
, rel
, ccon
->attnum
,
9714 !ccon
->is_no_inherit
, lockmode
);
9716 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9719 /* At this point we must have a locked-down name to use */
9720 Assert(newcons
== NIL
|| constr
->conname
!= NULL
);
9722 /* Advance command counter in case same table is visited multiple times */
9723 CommandCounterIncrement();
9726 * If the constraint got merged with an existing constraint, we're done.
9727 * We mustn't recurse to child tables in this case, because they've
9728 * already got the constraint, and visiting them again would lead to an
9729 * incorrect value for coninhcount.
9735 * If adding a NO INHERIT constraint, no need to find our children.
9737 if (constr
->is_no_inherit
)
9741 * Propagate to children as appropriate. Unlike most other ALTER
9742 * routines, we have to do this one level of recursion at a time; we can't
9743 * use find_all_inheritors to do it in one pass.
9746 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9749 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9750 * constraint creation only if there are no children currently. Error out
9753 if (!recurse
&& children
!= NIL
)
9755 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9756 errmsg("constraint must be added to child tables too")));
9759 * The constraint must appear as inherited in children, so create a
9760 * modified constraint object to use.
9762 constr
= copyObject(constr
);
9763 constr
->inhcount
= 1;
9764 foreach(child
, children
)
9766 Oid childrelid
= lfirst_oid(child
);
9768 AlteredTableInfo
*childtab
;
9770 /* find_inheritance_children already got lock */
9771 childrel
= table_open(childrelid
, NoLock
);
9772 CheckTableNotInUse(childrel
, "ALTER TABLE");
9774 /* Find or create work queue entry for this table */
9775 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9778 * Recurse to child. XXX if we didn't create a constraint on the
9779 * parent because it already existed, and we do create one on a child,
9780 * should we return that child's constraint ObjectAddress here?
9782 ATAddCheckNNConstraint(wqueue
, childtab
, childrel
,
9783 constr
, recurse
, true, is_readd
, lockmode
);
9785 table_close(childrel
, NoLock
);
9792 * Add a foreign-key constraint to a single table; return the new constraint's
9795 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9796 * lock on the rel, and have done appropriate validity checks for it.
9797 * We do permissions checks here, however.
9799 * When the referenced or referencing tables (or both) are partitioned,
9800 * multiple pg_constraint rows are required -- one for each partitioned table
9801 * and each partition on each side (fortunately, not one for every combination
9802 * thereof). We also need action triggers on each leaf partition on the
9803 * referenced side, and check triggers on each leaf partition on the
9806 static ObjectAddress
9807 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9808 Constraint
*fkconstraint
,
9809 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9812 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9813 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9814 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9815 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9816 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9817 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9818 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9819 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9820 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9822 bool pk_has_without_overlaps
;
9829 ObjectAddress address
;
9830 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9833 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9834 * delete rows out from under us.
9836 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9837 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9839 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9842 * Validity checks (permission checks wait till we have the column
9845 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9849 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9850 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9851 RelationGetRelationName(rel
),
9852 RelationGetRelationName(pkrel
))));
9853 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9855 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9856 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9857 RelationGetRelationName(rel
),
9858 RelationGetRelationName(pkrel
)),
9859 errdetail("This feature is not yet supported on partitioned tables.")));
9862 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9863 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9865 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9866 errmsg("referenced relation \"%s\" is not a table",
9867 RelationGetRelationName(pkrel
))));
9869 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9871 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9872 errmsg("permission denied: \"%s\" is a system catalog",
9873 RelationGetRelationName(pkrel
))));
9876 * References from permanent or unlogged tables to temp tables, and from
9877 * permanent tables to unlogged tables, are disallowed because the
9878 * referenced data can vanish out from under us. References from temp
9879 * tables to any other table type are also disallowed, because other
9880 * backends might need to run the RI triggers on the perm table, but they
9881 * can't reliably see tuples in the local buffers of other backends.
9883 switch (rel
->rd_rel
->relpersistence
)
9885 case RELPERSISTENCE_PERMANENT
:
9886 if (!RelationIsPermanent(pkrel
))
9888 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9889 errmsg("constraints on permanent tables may reference only permanent tables")));
9891 case RELPERSISTENCE_UNLOGGED
:
9892 if (!RelationIsPermanent(pkrel
)
9893 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9896 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9898 case RELPERSISTENCE_TEMP
:
9899 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9901 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9902 errmsg("constraints on temporary tables may reference only temporary tables")));
9903 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
9905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9906 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9911 * Look up the referencing attributes to make sure they exist, and record
9912 * their attnums and type OIDs.
9914 numfks
= transformColumnNameList(RelationGetRelid(rel
),
9915 fkconstraint
->fk_attrs
,
9916 fkattnum
, fktypoid
);
9917 with_period
= fkconstraint
->fk_with_period
|| fkconstraint
->pk_with_period
;
9918 if (with_period
&& !fkconstraint
->fk_with_period
)
9920 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9921 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9923 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
9924 fkconstraint
->fk_del_set_cols
,
9925 fkdelsetcols
, NULL
);
9926 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
9927 numfkdelsetcols
, fkdelsetcols
,
9928 fkconstraint
->fk_del_set_cols
);
9931 * If the attribute list for the referenced table was omitted, lookup the
9932 * definition of the primary key and use it. Otherwise, validate the
9933 * supplied attribute list. In either case, discover the index OID and
9934 * index opclasses, and the attnums and type OIDs of the attributes.
9936 if (fkconstraint
->pk_attrs
== NIL
)
9938 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
9939 &fkconstraint
->pk_attrs
,
9941 opclasses
, &pk_has_without_overlaps
);
9943 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9944 if (pk_has_without_overlaps
&& !fkconstraint
->fk_with_period
)
9946 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9947 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9951 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
9952 fkconstraint
->pk_attrs
,
9953 pkattnum
, pktypoid
);
9955 /* Since we got pk_attrs, one should be a period. */
9956 if (with_period
&& !fkconstraint
->pk_with_period
)
9958 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9959 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9961 /* Look for an index matching the column list */
9962 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
9963 with_period
, opclasses
, &pk_has_without_overlaps
);
9967 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9970 if (pk_has_without_overlaps
&& !with_period
)
9972 errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9973 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9976 * Now we can check permissions.
9978 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
9981 * Check some things for generated columns.
9983 for (i
= 0; i
< numfks
; i
++)
9985 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
9990 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9992 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9993 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
9994 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
9996 (errcode(ERRCODE_SYNTAX_ERROR
),
9997 errmsg("invalid %s action for foreign key constraint containing generated column",
9999 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
10000 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
10002 (errcode(ERRCODE_SYNTAX_ERROR
),
10003 errmsg("invalid %s action for foreign key constraint containing generated column",
10009 * Some actions are currently unsupported for foreign keys using PERIOD.
10011 if (fkconstraint
->fk_with_period
)
10013 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
||
10014 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
10015 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
)
10017 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
10018 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10021 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_CASCADE
||
10022 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
10023 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
10025 errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
10026 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10031 * Look up the equality operators to use in the constraint.
10033 * Note that we have to be careful about the difference between the actual
10034 * PK column type and the opclass' declared input type, which might be
10035 * only binary-compatible with it. The declared opcintype is the right
10036 * thing to probe pg_amop with.
10038 if (numfks
!= numpks
)
10040 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
10041 errmsg("number of referencing and referenced columns for foreign key disagree")));
10044 * On the strength of a previous constraint, we might avoid scanning
10045 * tables to validate this one. See below.
10047 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
10048 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
10050 for (i
= 0; i
< numpks
; i
++)
10052 Oid pktype
= pktypoid
[i
];
10053 Oid fktype
= fktypoid
[i
];
10056 Form_pg_opclass cla_tup
;
10066 /* We need several fields out of the pg_opclass entry */
10067 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
10068 if (!HeapTupleIsValid(cla_ht
))
10069 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
10070 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
10071 amid
= cla_tup
->opcmethod
;
10072 opfamily
= cla_tup
->opcfamily
;
10073 opcintype
= cla_tup
->opcintype
;
10074 ReleaseSysCache(cla_ht
);
10078 StrategyNumber rtstrategy
;
10079 bool for_overlaps
= with_period
&& i
== numpks
- 1;
10082 * GiST indexes are required to support temporal foreign keys
10083 * because they combine equals and overlaps.
10085 if (amid
!= GIST_AM_OID
)
10086 elog(ERROR
, "only GiST indexes are supported for temporal foreign keys");
10088 rtstrategy
= for_overlaps
? RTOverlapStrategyNumber
: RTEqualStrategyNumber
;
10091 * An opclass can use whatever strategy numbers it wants, so we
10092 * ask the opclass what number it actually uses instead of our RT*
10095 eqstrategy
= GistTranslateStratnum(opclasses
[i
], rtstrategy
);
10096 if (eqstrategy
== InvalidStrategy
)
10100 tuple
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
10101 if (!HeapTupleIsValid(tuple
))
10102 elog(ERROR
, "cache lookup failed for operator class %u", opclasses
[i
]);
10105 errcode(ERRCODE_UNDEFINED_OBJECT
),
10107 ? errmsg("could not identify an overlaps operator for foreign key")
10108 : errmsg("could not identify an equality operator for foreign key"),
10109 errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10110 rtstrategy
, NameStr(((Form_pg_opclass
) GETSTRUCT(tuple
))->opcname
), "gist"));
10116 * Check it's a btree; currently this can never fail since no
10117 * other index AMs support unique indexes. If we ever did have
10118 * other types of unique indexes, we'd need a way to determine
10119 * which operator strategy number is equality. (We could use
10120 * something like GistTranslateStratnum.)
10122 if (amid
!= BTREE_AM_OID
)
10123 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
10124 eqstrategy
= BTEqualStrategyNumber
;
10128 * There had better be a primary equality operator for the index.
10129 * We'll use it for PK = PK comparisons.
10131 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
10134 if (!OidIsValid(ppeqop
))
10135 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
10136 eqstrategy
, opcintype
, opcintype
, opfamily
);
10139 * Are there equality operators that take exactly the FK type? Assume
10140 * we should look through any domain here.
10142 fktyped
= getBaseType(fktype
);
10144 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
10146 if (OidIsValid(pfeqop
))
10148 pfeqop_right
= fktyped
;
10149 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
10154 /* keep compiler quiet */
10155 pfeqop_right
= InvalidOid
;
10156 ffeqop
= InvalidOid
;
10159 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10162 * Otherwise, look for an implicit cast from the FK type to the
10163 * opcintype, and if found, use the primary equality operator.
10164 * This is a bit tricky because opcintype might be a polymorphic
10165 * type such as ANYARRAY or ANYENUM; so what we have to test is
10166 * whether the two actual column types can be concurrently cast to
10167 * that type. (Otherwise, we'd fail to reject combinations such
10168 * as int[] and point[].)
10170 Oid input_typeids
[2];
10171 Oid target_typeids
[2];
10173 input_typeids
[0] = pktype
;
10174 input_typeids
[1] = fktype
;
10175 target_typeids
[0] = opcintype
;
10176 target_typeids
[1] = opcintype
;
10177 if (can_coerce_type(2, input_typeids
, target_typeids
,
10178 COERCION_IMPLICIT
))
10180 pfeqop
= ffeqop
= ppeqop
;
10181 pfeqop_right
= opcintype
;
10185 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
10187 (errcode(ERRCODE_DATATYPE_MISMATCH
),
10188 errmsg("foreign key constraint \"%s\" cannot be implemented",
10189 fkconstraint
->conname
),
10190 errdetail("Key columns \"%s\" and \"%s\" "
10191 "are of incompatible types: %s and %s.",
10192 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
10193 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
10194 format_type_be(fktype
),
10195 format_type_be(pktype
))));
10200 * When a pfeqop changes, revalidate the constraint. We could
10201 * permit intra-opfamily changes, but that adds subtle complexity
10202 * without any concrete benefit for core types. We need not
10203 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10205 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
10206 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
10213 CoercionPathType old_pathtype
;
10214 CoercionPathType new_pathtype
;
10217 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
10221 * Identify coercion pathways from each of the old and new FK-side
10222 * column types to the right (foreign) operand type of the pfeqop.
10223 * We may assume that pg_constraint.conkey is not changing.
10225 old_fktype
= attr
->atttypid
;
10226 new_fktype
= fktype
;
10227 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
10229 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
10233 * Upon a change to the cast from the FK column to its pfeqop
10234 * operand, revalidate the constraint. For this evaluation, a
10235 * binary coercion cast is equivalent to no cast at all. While
10236 * type implementors should design implicit casts with an eye
10237 * toward consistency of operations like equality, we cannot
10238 * assume here that they have done so.
10240 * A function with a polymorphic argument could change behavior
10241 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10242 * when the cast destination is polymorphic, we only avoid
10243 * revalidation if the input type has not changed at all. Given
10244 * just the core data types and operator classes, this requirement
10245 * prevents no would-be optimizations.
10247 * If the cast converts from a base type to a domain thereon, then
10248 * that domain type must be the opcintype of the unique index.
10249 * Necessarily, the primary key column must then be of the domain
10250 * type. Since the constraint was previously valid, all values on
10251 * the foreign side necessarily exist on the primary side and in
10252 * turn conform to the domain. Consequently, we need not treat
10253 * domains specially here.
10255 * Since we require that all collations share the same notion of
10256 * equality (which they do, because texteq reduces to bitwise
10257 * equality), we don't compare collation here.
10259 * We need not directly consider the PK type. It's necessarily
10260 * binary coercible to the opcintype of the unique index column,
10261 * and ri_triggers.c will only deal with PK datums in terms of
10262 * that opcintype. Changing the opcintype also changes pfeqop.
10264 old_check_ok
= (new_pathtype
== old_pathtype
&&
10265 new_castfunc
== old_castfunc
&&
10266 (!IsPolymorphicType(pfeqop_right
) ||
10267 new_fktype
== old_fktype
));
10270 pfeqoperators
[i
] = pfeqop
;
10271 ppeqoperators
[i
] = ppeqop
;
10272 ffeqoperators
[i
] = ffeqop
;
10276 * For FKs with PERIOD we need additional operators to check whether the
10277 * referencing row's range is contained by the aggregated ranges of the
10278 * referenced row(s). For rangetypes and multirangetypes this is
10279 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10280 * support for now. FKs will look these up at "runtime", but we should
10281 * make sure the lookup works here, even if we don't use the values.
10286 Oid aggedperiodoperoid
;
10288 FindFKPeriodOpers(opclasses
[numpks
- 1], &periodoperoid
, &aggedperiodoperoid
);
10292 * Create all the constraint and trigger objects, recursing to partitions
10293 * as necessary. First handle the referenced side.
10295 address
= addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, pkrel
,
10297 InvalidOid
, /* no parent constraint */
10307 InvalidOid
, InvalidOid
,
10310 /* Now handle the referencing side. */
10311 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
10324 InvalidOid
, InvalidOid
,
10328 * Done. Close pk table, but keep lock until we've committed.
10330 table_close(pkrel
, NoLock
);
10336 * validateFkOnDeleteSetColumns
10337 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10338 * column lists are valid.
10341 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
10342 int numfksetcols
, const int16
*fksetcolsattnums
,
10345 for (int i
= 0; i
< numfksetcols
; i
++)
10347 int16 setcol_attnum
= fksetcolsattnums
[i
];
10350 for (int j
= 0; j
< numfks
; j
++)
10352 if (fkattnums
[j
] == setcol_attnum
)
10361 char *col
= strVal(list_nth(fksetcols
, i
));
10364 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
10365 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
10371 * addFkRecurseReferenced
10372 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10373 * side of the constraint
10375 * Create pg_constraint rows for the referenced side of the constraint,
10376 * referencing the parent of the referencing side; also create action triggers
10377 * on leaf partitions. If the table is partitioned, recurse to handle each
10380 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10381 * of an ALTER TABLE sequence.
10382 * fkconstraint is the constraint being added.
10383 * rel is the root referencing relation.
10384 * pkrel is the referenced relation; might be a partition, if recursing.
10385 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10386 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10387 * top-level constraint.
10388 * numfks is the number of columns in the foreign key
10389 * pkattnum is the attnum array of referenced attributes.
10390 * fkattnum is the attnum array of referencing attributes.
10391 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10393 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10394 * NULL/DEFAULT clause
10395 * pf/pp/ffeqoperators are OID array of operators between columns.
10396 * old_check_ok signals that this constraint replaces an existing one that
10397 * was already validated (thus this one doesn't need validation).
10398 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10399 * a partition, are the OIDs of the parent action triggers for DELETE and
10400 * UPDATE respectively.
10402 static ObjectAddress
10403 addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10404 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10406 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
10407 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10408 int numfkdelsetcols
, int16
*fkdelsetcols
,
10410 Oid parentDelTrigger
, Oid parentUpdTrigger
,
10413 ObjectAddress address
;
10419 Oid deleteTriggerOid
,
10423 * Verify relkind for each referenced partition. At the top level, this
10424 * is redundant with a previous check, but we need it when recursing.
10426 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
10427 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
10429 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10430 errmsg("referenced relation \"%s\" is not a table",
10431 RelationGetRelationName(pkrel
))));
10434 * Caller supplies us with a constraint name; however, it may be used in
10435 * this partition, so come up with a different one in that case.
10437 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10438 RelationGetRelid(rel
),
10439 fkconstraint
->conname
))
10440 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
10441 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10443 RelationGetNamespace(rel
), NIL
);
10445 conname
= fkconstraint
->conname
;
10447 if (OidIsValid(parentConstr
))
10449 conislocal
= false;
10451 connoinherit
= false;
10459 * always inherit for partitioned tables, never for legacy inheritance
10461 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
10465 * Record the FK constraint in pg_constraint.
10467 constrOid
= CreateConstraintEntry(conname
,
10468 RelationGetNamespace(rel
),
10469 CONSTRAINT_FOREIGN
,
10470 fkconstraint
->deferrable
,
10471 fkconstraint
->initdeferred
,
10472 fkconstraint
->initially_valid
,
10474 RelationGetRelid(rel
),
10478 InvalidOid
, /* not a domain constraint */
10480 RelationGetRelid(pkrel
),
10486 fkconstraint
->fk_upd_action
,
10487 fkconstraint
->fk_del_action
,
10490 fkconstraint
->fk_matchtype
,
10491 NULL
, /* no exclusion constraint */
10492 NULL
, /* no check constraint */
10494 conislocal
, /* islocal */
10495 coninhcount
, /* inhcount */
10496 connoinherit
, /* conNoInherit */
10497 with_period
, /* conPeriod */
10498 false); /* is_internal */
10500 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10503 * Mark the child constraint as part of the parent constraint; it must not
10504 * be dropped on its own. (This constraint is deleted when the partition
10505 * is detached, but a special check needs to occur that the partition
10506 * contains no referenced values.)
10508 if (OidIsValid(parentConstr
))
10510 ObjectAddress referenced
;
10512 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10513 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
10516 /* make new constraint visible, in case we add more */
10517 CommandCounterIncrement();
10520 * Create the action triggers that enforce the constraint.
10522 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
10524 constrOid
, indexOid
,
10525 parentDelTrigger
, parentUpdTrigger
,
10526 &deleteTriggerOid
, &updateTriggerOid
);
10529 * If the referenced table is partitioned, recurse on ourselves to handle
10530 * each partition. We need one pg_constraint row created for each
10531 * partition in addition to the pg_constraint row for the parent table.
10533 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10535 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
10537 for (int i
= 0; i
< pd
->nparts
; i
++)
10541 AttrNumber
*mapped_pkattnum
;
10544 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
10547 * Map the attribute numbers in the referenced side of the FK
10548 * definition to match the partition's column layout.
10550 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
10551 RelationGetDescr(pkrel
),
10555 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
10556 for (int j
= 0; j
< numfks
; j
++)
10557 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
10560 mapped_pkattnum
= pkattnum
;
10563 partIndexId
= index_get_partition(partRel
, indexOid
);
10564 if (!OidIsValid(partIndexId
))
10565 elog(ERROR
, "index for %u not found in partition %s",
10566 indexOid
, RelationGetRelationName(partRel
));
10567 addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, partRel
,
10568 partIndexId
, constrOid
, numfks
,
10569 mapped_pkattnum
, fkattnum
,
10570 pfeqoperators
, ppeqoperators
, ffeqoperators
,
10571 numfkdelsetcols
, fkdelsetcols
,
10573 deleteTriggerOid
, updateTriggerOid
,
10576 /* Done -- clean up (but keep the lock) */
10577 table_close(partRel
, NoLock
);
10580 pfree(mapped_pkattnum
);
10590 * addFkRecurseReferencing
10591 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10593 * If the referencing relation is a plain relation, create the necessary check
10594 * triggers that implement the constraint, and set up for Phase 3 constraint
10595 * verification. If the referencing relation is a partitioned table, then
10596 * we create a pg_constraint row for it and recurse on this routine for each
10599 * We assume that the referenced relation is locked against concurrent
10600 * deletions. If it's a partitioned relation, every partition must be so
10603 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10604 * of an ALTER TABLE sequence.
10605 * fkconstraint is the constraint being added.
10606 * rel is the referencing relation; might be a partition, if recursing.
10607 * pkrel is the root referenced relation.
10608 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10609 * parentConstr is the OID of the parent constraint (there is always one).
10610 * numfks is the number of columns in the foreign key
10611 * pkattnum is the attnum array of referenced attributes.
10612 * fkattnum is the attnum array of referencing attributes.
10613 * pf/pp/ffeqoperators are OID array of operators between columns.
10614 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10616 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10617 * NULL/DEFAULT clause
10618 * old_check_ok signals that this constraint replaces an existing one that
10619 * was already validated (thus this one doesn't need validation).
10620 * lockmode is the lockmode to acquire on partitions when recursing.
10621 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10622 * a partition, are the OIDs of the parent check triggers for INSERT and
10623 * UPDATE respectively.
10626 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10627 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10628 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
10629 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10630 int numfkdelsetcols
, int16
*fkdelsetcols
,
10631 bool old_check_ok
, LOCKMODE lockmode
,
10632 Oid parentInsTrigger
, Oid parentUpdTrigger
,
10635 Oid insertTriggerOid
,
10638 Assert(OidIsValid(parentConstr
));
10640 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10642 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10643 errmsg("foreign key constraints are not supported on foreign tables")));
10646 * Add the check triggers to it and, if necessary, schedule it to be
10647 * checked in Phase 3.
10649 * If the relation is partitioned, drill down to do it to its partitions.
10651 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
10652 RelationGetRelid(pkrel
),
10656 parentInsTrigger
, parentUpdTrigger
,
10657 &insertTriggerOid
, &updateTriggerOid
);
10659 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
10662 * Tell Phase 3 to check that the constraint is satisfied by existing
10663 * rows. We can skip this during table creation, when requested
10664 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10665 * and when we're recreating a constraint following a SET DATA TYPE
10666 * operation that did not impugn its validity.
10668 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
10670 NewConstraint
*newcon
;
10671 AlteredTableInfo
*tab
;
10673 tab
= ATGetQueueEntry(wqueue
, rel
);
10675 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
10676 newcon
->name
= get_constraint_name(parentConstr
);
10677 newcon
->contype
= CONSTR_FOREIGN
;
10678 newcon
->refrelid
= RelationGetRelid(pkrel
);
10679 newcon
->refindid
= indexOid
;
10680 newcon
->conid
= parentConstr
;
10681 newcon
->conwithperiod
= fkconstraint
->fk_with_period
;
10682 newcon
->qual
= (Node
*) fkconstraint
;
10684 tab
->constraints
= lappend(tab
->constraints
, newcon
);
10687 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10689 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
10693 * Triggers of the foreign keys will be manipulated a bunch of times
10694 * in the loop below. To avoid repeatedly opening/closing the trigger
10695 * catalog relation, we open it here and pass it to the subroutines
10698 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10701 * Recurse to take appropriate action on each partition; either we
10702 * find an existing constraint to reparent to ours, or we create a new
10705 for (int i
= 0; i
< pd
->nparts
; i
++)
10707 Oid partitionId
= pd
->oids
[i
];
10708 Relation partition
= table_open(partitionId
, lockmode
);
10711 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
10715 ObjectAddress address
,
10719 CheckTableNotInUse(partition
, "ALTER TABLE");
10721 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
10722 RelationGetDescr(rel
),
10724 for (int j
= 0; j
< numfks
; j
++)
10725 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
10727 /* Check whether an existing constraint can be repurposed */
10728 partFKs
= copyObject(RelationGetFKeyList(partition
));
10730 foreach(cell
, partFKs
)
10732 ForeignKeyCacheInfo
*fk
;
10734 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
10735 if (tryAttachPartitionForeignKey(fk
,
10752 table_close(partition
, NoLock
);
10757 * No luck finding a good constraint to reuse; create our own.
10759 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10760 RelationGetRelid(partition
),
10761 fkconstraint
->conname
))
10762 conname
= ChooseConstraintName(RelationGetRelationName(partition
),
10763 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10765 RelationGetNamespace(partition
), NIL
);
10767 conname
= fkconstraint
->conname
;
10769 CreateConstraintEntry(conname
,
10770 RelationGetNamespace(partition
),
10771 CONSTRAINT_FOREIGN
,
10772 fkconstraint
->deferrable
,
10773 fkconstraint
->initdeferred
,
10774 fkconstraint
->initially_valid
,
10782 RelationGetRelid(pkrel
),
10788 fkconstraint
->fk_upd_action
,
10789 fkconstraint
->fk_del_action
,
10792 fkconstraint
->fk_matchtype
,
10799 with_period
, /* conPeriod */
10803 * Give this constraint partition-type dependencies on the parent
10804 * constraint as well as the table.
10806 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10807 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10808 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10809 ObjectAddressSet(referenced
, RelationRelationId
, partitionId
);
10810 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10812 /* Make all this visible before recursing */
10813 CommandCounterIncrement();
10815 /* call ourselves to finalize the creation and we're done */
10816 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10833 table_close(partition
, NoLock
);
10836 table_close(trigrel
, RowExclusiveLock
);
10841 * CloneForeignKeyConstraints
10842 * Clone foreign keys from a partitioned table to a newly acquired
10845 * partitionRel is a partition of parentRel, so we can be certain that it has
10846 * the same columns with the same datatypes. The columns may be in different
10849 * wqueue must be passed to set up phase 3 constraint checking, unless the
10850 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10854 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10855 Relation partitionRel
)
10857 /* This only works for declarative partitioning */
10858 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10861 * Clone constraints for which the parent is on the referenced side.
10863 CloneFkReferenced(parentRel
, partitionRel
);
10866 * Now clone constraints where the parent is on the referencing side.
10868 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10872 * CloneFkReferenced
10873 * Subroutine for CloneForeignKeyConstraints
10875 * Find all the FKs that have the parent relation on the referenced side;
10876 * clone those constraints to the given partition. This is to be called
10877 * when the partition is being created or attached.
10879 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10881 * This recurses to partitions, if the relation being attached is partitioned.
10882 * Recursion is done by calling addFkRecurseReferenced.
10885 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10887 Relation pg_constraint
;
10891 ScanKeyData key
[2];
10897 * Search for any constraints where this partition's parent is in the
10898 * referenced side. However, we must not clone any constraint whose
10899 * parent constraint is also going to be cloned, to avoid duplicates. So
10900 * do it in two steps: first construct the list of constraints to clone,
10901 * then go over that list cloning those whose parents are not in the list.
10902 * (We must not rely on the parent being seen first, since the catalog
10903 * scan could return children first.)
10905 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
10906 ScanKeyInit(&key
[0],
10907 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
10908 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
10909 ScanKeyInit(&key
[1],
10910 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
10911 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
10912 /* This is a seqscan, as we don't have a usable index ... */
10913 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
10915 while ((tuple
= systable_getnext(scan
)) != NULL
)
10917 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10919 clone
= lappend_oid(clone
, constrForm
->oid
);
10921 systable_endscan(scan
);
10922 table_close(pg_constraint
, RowShareLock
);
10925 * Triggers of the foreign keys will be manipulated a bunch of times in
10926 * the loop below. To avoid repeatedly opening/closing the trigger
10927 * catalog relation, we open it here and pass it to the subroutines called
10930 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10932 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
10933 RelationGetDescr(parentRel
),
10935 foreach(cell
, clone
)
10937 Oid constrOid
= lfirst_oid(cell
);
10938 Form_pg_constraint constrForm
;
10943 AttrNumber conkey
[INDEX_MAX_KEYS
];
10944 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
10945 AttrNumber confkey
[INDEX_MAX_KEYS
];
10946 Oid conpfeqop
[INDEX_MAX_KEYS
];
10947 Oid conppeqop
[INDEX_MAX_KEYS
];
10948 Oid conffeqop
[INDEX_MAX_KEYS
];
10949 int numfkdelsetcols
;
10950 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10951 Constraint
*fkconstraint
;
10952 Oid deleteTriggerOid
,
10955 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
10956 if (!HeapTupleIsValid(tuple
))
10957 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
10958 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10961 * As explained above: don't try to clone a constraint for which we're
10962 * going to clone the parent.
10964 if (list_member_oid(clone
, constrForm
->conparentid
))
10966 ReleaseSysCache(tuple
);
10971 * Don't clone self-referencing foreign keys, which can be in the
10972 * partitioned table or in the partition-to-be.
10974 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
10975 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
10977 ReleaseSysCache(tuple
);
10982 * Because we're only expanding the key space at the referenced side,
10983 * we don't need to prevent any operation in the referencing table, so
10984 * AccessShareLock suffices (assumes that dropping the constraint
10987 fkRel
= table_open(constrForm
->conrelid
, AccessShareLock
);
10989 indexOid
= constrForm
->conindid
;
10990 DeconstructFkConstraintRow(tuple
,
11000 for (int i
= 0; i
< numfks
; i
++)
11001 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
11003 fkconstraint
= makeNode(Constraint
);
11004 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11005 fkconstraint
->conname
= NameStr(constrForm
->conname
);
11006 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11007 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11008 fkconstraint
->location
= -1;
11009 fkconstraint
->pktable
= NULL
;
11010 /* ->fk_attrs determined below */
11011 fkconstraint
->pk_attrs
= NIL
;
11012 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11013 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11014 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11015 fkconstraint
->fk_del_set_cols
= NIL
;
11016 fkconstraint
->old_conpfeqop
= NIL
;
11017 fkconstraint
->old_pktable_oid
= InvalidOid
;
11018 fkconstraint
->skip_validation
= false;
11019 fkconstraint
->initially_valid
= true;
11021 /* set up colnames that are used to generate the constraint name */
11022 for (int i
= 0; i
< numfks
; i
++)
11024 Form_pg_attribute att
;
11026 att
= TupleDescAttr(RelationGetDescr(fkRel
),
11028 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11029 makeString(NameStr(att
->attname
)));
11033 * Add the new foreign key constraint pointing to the new partition.
11034 * Because this new partition appears in the referenced side of the
11035 * constraint, we don't need to set up for Phase 3 check.
11037 partIndexId
= index_get_partition(partitionRel
, indexOid
);
11038 if (!OidIsValid(partIndexId
))
11039 elog(ERROR
, "index for %u not found in partition %s",
11040 indexOid
, RelationGetRelationName(partitionRel
));
11043 * Get the "action" triggers belonging to the constraint to pass as
11044 * parent OIDs for similar triggers that will be created on the
11045 * partition in addFkRecurseReferenced().
11047 GetForeignKeyActionTriggers(trigrel
, constrOid
,
11048 constrForm
->confrelid
, constrForm
->conrelid
,
11049 &deleteTriggerOid
, &updateTriggerOid
);
11051 addFkRecurseReferenced(NULL
,
11068 constrForm
->conperiod
);
11070 table_close(fkRel
, NoLock
);
11071 ReleaseSysCache(tuple
);
11074 table_close(trigrel
, RowExclusiveLock
);
11078 * CloneFkReferencing
11079 * Subroutine for CloneForeignKeyConstraints
11081 * For each FK constraint of the parent relation in the given list, find an
11082 * equivalent constraint in its partition relation that can be reparented;
11083 * if one cannot be found, create a new constraint in the partition as its
11086 * If wqueue is given, it is used to set up phase-3 verification for each
11087 * cloned constraint; if omitted, we assume that such verification is not
11088 * needed (example: the partition is being created anew).
11091 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
11099 /* obtain a list of constraints that we need to clone */
11100 foreach(cell
, RelationGetFKeyList(parentRel
))
11102 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
11104 clone
= lappend_oid(clone
, fk
->conoid
);
11108 * Silently do nothing if there's nothing to do. In particular, this
11109 * avoids throwing a spurious error for foreign tables.
11114 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
11116 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11117 errmsg("foreign key constraints are not supported on foreign tables")));
11120 * Triggers of the foreign keys will be manipulated a bunch of times in
11121 * the loop below. To avoid repeatedly opening/closing the trigger
11122 * catalog relation, we open it here and pass it to the subroutines called
11125 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11128 * The constraint key may differ, if the columns in the partition are
11129 * different. This map is used to convert them.
11131 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
11132 RelationGetDescr(parentRel
),
11135 partFKs
= copyObject(RelationGetFKeyList(partRel
));
11137 foreach(cell
, clone
)
11139 Oid parentConstrOid
= lfirst_oid(cell
);
11140 Form_pg_constraint constrForm
;
11144 AttrNumber conkey
[INDEX_MAX_KEYS
];
11145 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
11146 AttrNumber confkey
[INDEX_MAX_KEYS
];
11147 Oid conpfeqop
[INDEX_MAX_KEYS
];
11148 Oid conppeqop
[INDEX_MAX_KEYS
];
11149 Oid conffeqop
[INDEX_MAX_KEYS
];
11150 int numfkdelsetcols
;
11151 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
11152 Constraint
*fkconstraint
;
11156 ObjectAddress address
,
11159 Oid insertTriggerOid
,
11163 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
11164 if (!HeapTupleIsValid(tuple
))
11165 elog(ERROR
, "cache lookup failed for constraint %u",
11167 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11169 /* Don't clone constraints whose parents are being cloned */
11170 if (list_member_oid(clone
, constrForm
->conparentid
))
11172 ReleaseSysCache(tuple
);
11177 * Need to prevent concurrent deletions. If pkrel is a partitioned
11178 * relation, that means to lock all partitions.
11180 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
11181 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11182 (void) find_all_inheritors(RelationGetRelid(pkrel
),
11183 ShareRowExclusiveLock
, NULL
);
11185 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
11186 conpfeqop
, conppeqop
, conffeqop
,
11187 &numfkdelsetcols
, confdelsetcols
);
11188 for (int i
= 0; i
< numfks
; i
++)
11189 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
11192 * Get the "check" triggers belonging to the constraint to pass as
11193 * parent OIDs for similar triggers that will be created on the
11194 * partition in addFkRecurseReferencing(). They are also passed to
11195 * tryAttachPartitionForeignKey() below to simply assign as parents to
11196 * the partition's existing "check" triggers, that is, if the
11197 * corresponding constraints is deemed attachable to the parent
11200 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
11201 constrForm
->confrelid
, constrForm
->conrelid
,
11202 &insertTriggerOid
, &updateTriggerOid
);
11205 * Before creating a new constraint, see whether any existing FKs are
11206 * fit for the purpose. If one is, attach the parent constraint to
11207 * it, and don't clone anything. This way we avoid the expensive
11208 * verification step and don't end up with a duplicate FK, and we
11209 * don't need to recurse to partitions for this constraint.
11212 foreach(lc
, partFKs
)
11214 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
11216 if (tryAttachPartitionForeignKey(fk
,
11217 RelationGetRelid(partRel
),
11228 table_close(pkrel
, NoLock
);
11234 ReleaseSysCache(tuple
);
11238 /* No dice. Set up to create our own constraint */
11239 fkconstraint
= makeNode(Constraint
);
11240 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
11241 /* ->conname determined below */
11242 fkconstraint
->deferrable
= constrForm
->condeferrable
;
11243 fkconstraint
->initdeferred
= constrForm
->condeferred
;
11244 fkconstraint
->location
= -1;
11245 fkconstraint
->pktable
= NULL
;
11246 /* ->fk_attrs determined below */
11247 fkconstraint
->pk_attrs
= NIL
;
11248 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
11249 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
11250 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
11251 fkconstraint
->fk_del_set_cols
= NIL
;
11252 fkconstraint
->old_conpfeqop
= NIL
;
11253 fkconstraint
->old_pktable_oid
= InvalidOid
;
11254 fkconstraint
->skip_validation
= false;
11255 fkconstraint
->initially_valid
= true;
11256 for (int i
= 0; i
< numfks
; i
++)
11258 Form_pg_attribute att
;
11260 att
= TupleDescAttr(RelationGetDescr(partRel
),
11261 mapped_conkey
[i
] - 1);
11262 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
11263 makeString(NameStr(att
->attname
)));
11265 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
11266 RelationGetRelid(partRel
),
11267 NameStr(constrForm
->conname
)))
11268 fkconstraint
->conname
=
11269 ChooseConstraintName(RelationGetRelationName(partRel
),
11270 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
11272 RelationGetNamespace(partRel
), NIL
);
11274 fkconstraint
->conname
= pstrdup(NameStr(constrForm
->conname
));
11276 indexOid
= constrForm
->conindid
;
11277 with_period
= constrForm
->conperiod
;
11279 CreateConstraintEntry(fkconstraint
->conname
,
11280 constrForm
->connamespace
,
11281 CONSTRAINT_FOREIGN
,
11282 fkconstraint
->deferrable
,
11283 fkconstraint
->initdeferred
,
11284 constrForm
->convalidated
,
11286 RelationGetRelid(partRel
),
11290 InvalidOid
, /* not a domain constraint */
11292 constrForm
->confrelid
, /* same foreign rel */
11298 fkconstraint
->fk_upd_action
,
11299 fkconstraint
->fk_del_action
,
11302 fkconstraint
->fk_matchtype
,
11306 false, /* islocal */
11308 false, /* conNoInherit */
11309 with_period
, /* conPeriod */
11312 /* Set up partition dependencies for the new constraint */
11313 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
11314 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstrOid
);
11315 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
11316 ObjectAddressSet(referenced
, RelationRelationId
,
11317 RelationGetRelid(partRel
));
11318 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
11320 /* Done with the cloned constraint's tuple */
11321 ReleaseSysCache(tuple
);
11323 /* Make all this visible before recursing */
11324 CommandCounterIncrement();
11326 addFkRecurseReferencing(wqueue
,
11340 false, /* no old check exists */
11341 AccessExclusiveLock
,
11345 table_close(pkrel
, NoLock
);
11348 table_close(trigrel
, RowExclusiveLock
);
11352 * When the parent of a partition receives [the referencing side of] a foreign
11353 * key, we must propagate that foreign key to the partition. However, the
11354 * partition might already have an equivalent foreign key; this routine
11355 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11356 * by the other parameters. If they are equivalent, create the link between
11357 * the two constraints and return true.
11359 * If the given FK does not match the one defined by rest of the params,
11363 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
11365 Oid parentConstrOid
,
11367 AttrNumber
*mapped_conkey
,
11368 AttrNumber
*confkey
,
11370 Oid parentInsTrigger
,
11371 Oid parentUpdTrigger
,
11374 HeapTuple parentConstrTup
;
11375 Form_pg_constraint parentConstr
;
11376 HeapTuple partcontup
;
11377 Form_pg_constraint partConstr
;
11381 Oid insertTriggerOid
,
11384 parentConstrTup
= SearchSysCache1(CONSTROID
,
11385 ObjectIdGetDatum(parentConstrOid
));
11386 if (!HeapTupleIsValid(parentConstrTup
))
11387 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
11388 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
11391 * Do some quick & easy initial checks. If any of these fail, we cannot
11392 * use this constraint.
11394 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
11396 ReleaseSysCache(parentConstrTup
);
11399 for (int i
= 0; i
< numfks
; i
++)
11401 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
11402 fk
->confkey
[i
] != confkey
[i
] ||
11403 fk
->conpfeqop
[i
] != conpfeqop
[i
])
11405 ReleaseSysCache(parentConstrTup
);
11411 * Looks good so far; do some more extensive checks. Presumably the check
11412 * for 'convalidated' could be dropped, since we don't really care about
11413 * that, but let's be careful for now.
11415 partcontup
= SearchSysCache1(CONSTROID
,
11416 ObjectIdGetDatum(fk
->conoid
));
11417 if (!HeapTupleIsValid(partcontup
))
11418 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
11419 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
11420 if (OidIsValid(partConstr
->conparentid
) ||
11421 !partConstr
->convalidated
||
11422 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
11423 partConstr
->condeferred
!= parentConstr
->condeferred
||
11424 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
11425 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
11426 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
11428 ReleaseSysCache(parentConstrTup
);
11429 ReleaseSysCache(partcontup
);
11433 ReleaseSysCache(partcontup
);
11434 ReleaseSysCache(parentConstrTup
);
11437 * Looks good! Attach this constraint. The action triggers in the new
11438 * partition become redundant -- the parent table already has equivalent
11439 * ones, and those will be able to reach the partition. Remove the ones
11440 * in the partition. We identify them because they have our constraint
11441 * OID, as well as being on the referenced rel.
11444 Anum_pg_trigger_tgconstraint
,
11445 BTEqualStrategyNumber
, F_OIDEQ
,
11446 ObjectIdGetDatum(fk
->conoid
));
11447 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11449 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11451 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11452 ObjectAddress trigger
;
11454 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
11456 if (trgform
->tgrelid
!= fk
->confrelid
)
11460 * The constraint is originally set up to contain this trigger as an
11461 * implementation object, so there's a dependency record that links
11462 * the two; however, since the trigger is no longer needed, we remove
11463 * the dependency link in order to be able to drop the trigger while
11464 * keeping the constraint intact.
11466 deleteDependencyRecordsFor(TriggerRelationId
,
11469 /* make dependency deletion visible to performDeletion */
11470 CommandCounterIncrement();
11471 ObjectAddressSet(trigger
, TriggerRelationId
,
11473 performDeletion(&trigger
, DROP_RESTRICT
, 0);
11474 /* make trigger drop visible, in case the loop iterates */
11475 CommandCounterIncrement();
11478 systable_endscan(scan
);
11480 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
11483 * Like the constraint, attach partition's "check" triggers to the
11484 * corresponding parent triggers.
11486 GetForeignKeyCheckTriggers(trigrel
,
11487 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
11488 &insertTriggerOid
, &updateTriggerOid
);
11489 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
11490 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
11492 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
11493 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
11496 CommandCounterIncrement();
11501 * GetForeignKeyActionTriggers
11502 * Returns delete and update "action" triggers of the given relation
11503 * belonging to the given constraint
11506 GetForeignKeyActionTriggers(Relation trigrel
,
11507 Oid conoid
, Oid confrelid
, Oid conrelid
,
11508 Oid
*deleteTriggerOid
,
11509 Oid
*updateTriggerOid
)
11515 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
11517 Anum_pg_trigger_tgconstraint
,
11518 BTEqualStrategyNumber
, F_OIDEQ
,
11519 ObjectIdGetDatum(conoid
));
11521 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11523 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11525 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11527 if (trgform
->tgconstrrelid
!= conrelid
)
11529 if (trgform
->tgrelid
!= confrelid
)
11531 /* Only ever look at "action" triggers on the PK side. */
11532 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
11534 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
11536 Assert(*deleteTriggerOid
== InvalidOid
);
11537 *deleteTriggerOid
= trgform
->oid
;
11539 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11541 Assert(*updateTriggerOid
== InvalidOid
);
11542 *updateTriggerOid
= trgform
->oid
;
11544 #ifndef USE_ASSERT_CHECKING
11545 /* In an assert-enabled build, continue looking to find duplicates */
11546 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
11551 if (!OidIsValid(*deleteTriggerOid
))
11552 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
11554 if (!OidIsValid(*updateTriggerOid
))
11555 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
11558 systable_endscan(scan
);
11562 * GetForeignKeyCheckTriggers
11563 * Returns insert and update "check" triggers of the given relation
11564 * belonging to the given constraint
11567 GetForeignKeyCheckTriggers(Relation trigrel
,
11568 Oid conoid
, Oid confrelid
, Oid conrelid
,
11569 Oid
*insertTriggerOid
,
11570 Oid
*updateTriggerOid
)
11576 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
11578 Anum_pg_trigger_tgconstraint
,
11579 BTEqualStrategyNumber
, F_OIDEQ
,
11580 ObjectIdGetDatum(conoid
));
11582 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11584 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11586 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11588 if (trgform
->tgconstrrelid
!= confrelid
)
11590 if (trgform
->tgrelid
!= conrelid
)
11592 /* Only ever look at "check" triggers on the FK side. */
11593 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
11595 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
11597 Assert(*insertTriggerOid
== InvalidOid
);
11598 *insertTriggerOid
= trgform
->oid
;
11600 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11602 Assert(*updateTriggerOid
== InvalidOid
);
11603 *updateTriggerOid
= trgform
->oid
;
11605 #ifndef USE_ASSERT_CHECKING
11606 /* In an assert-enabled build, continue looking to find duplicates. */
11607 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
11612 if (!OidIsValid(*insertTriggerOid
))
11613 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
11615 if (!OidIsValid(*updateTriggerOid
))
11616 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
11619 systable_endscan(scan
);
11623 * ALTER TABLE ALTER CONSTRAINT
11625 * Update the attributes of a constraint.
11627 * Currently only works for Foreign Key constraints.
11629 * If the constraint is modified, returns its address; otherwise, return
11630 * InvalidObjectAddress.
11632 static ObjectAddress
11633 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
11634 bool recursing
, LOCKMODE lockmode
)
11636 Constraint
*cmdcon
;
11640 ScanKeyData skey
[3];
11641 HeapTuple contuple
;
11642 Form_pg_constraint currcon
;
11643 ObjectAddress address
;
11644 List
*otherrelids
= NIL
;
11647 cmdcon
= castNode(Constraint
, cmd
->def
);
11649 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11650 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11653 * Find and check the target constraint
11655 ScanKeyInit(&skey
[0],
11656 Anum_pg_constraint_conrelid
,
11657 BTEqualStrategyNumber
, F_OIDEQ
,
11658 ObjectIdGetDatum(RelationGetRelid(rel
)));
11659 ScanKeyInit(&skey
[1],
11660 Anum_pg_constraint_contypid
,
11661 BTEqualStrategyNumber
, F_OIDEQ
,
11662 ObjectIdGetDatum(InvalidOid
));
11663 ScanKeyInit(&skey
[2],
11664 Anum_pg_constraint_conname
,
11665 BTEqualStrategyNumber
, F_NAMEEQ
,
11666 CStringGetDatum(cmdcon
->conname
));
11667 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11668 true, NULL
, 3, skey
);
11670 /* There can be at most one matching row */
11671 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
11673 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11674 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11675 cmdcon
->conname
, RelationGetRelationName(rel
))));
11677 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11678 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
11680 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11681 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11682 cmdcon
->conname
, RelationGetRelationName(rel
))));
11685 * If it's not the topmost constraint, raise an error.
11687 * Altering a non-topmost constraint leaves some triggers untouched, since
11688 * they are not directly connected to this constraint; also, pg_dump would
11689 * ignore the deferrability status of the individual constraint, since it
11690 * only dumps topmost constraints. Avoid these problems by refusing this
11691 * operation and telling the user to alter the parent constraint instead.
11693 if (OidIsValid(currcon
->conparentid
))
11696 Oid parent
= currcon
->conparentid
;
11697 char *ancestorname
= NULL
;
11698 char *ancestortable
= NULL
;
11700 /* Loop to find the topmost constraint */
11701 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
11703 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
11705 /* If no parent, this is the constraint we want */
11706 if (!OidIsValid(contup
->conparentid
))
11708 ancestorname
= pstrdup(NameStr(contup
->conname
));
11709 ancestortable
= get_rel_name(contup
->conrelid
);
11710 ReleaseSysCache(tp
);
11714 parent
= contup
->conparentid
;
11715 ReleaseSysCache(tp
);
11719 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11720 cmdcon
->conname
, RelationGetRelationName(rel
)),
11721 ancestorname
&& ancestortable
?
11722 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11723 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
11724 errhint("You may alter the constraint it derives from instead.")));
11728 * Do the actual catalog work. We can skip changing if already in the
11729 * desired state, but not if a partitioned table: partitions need to be
11730 * processed regardless, in case they had the constraint locally changed.
11732 address
= InvalidObjectAddress
;
11733 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11734 currcon
->condeferred
!= cmdcon
->initdeferred
||
11735 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11737 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
11738 &otherrelids
, lockmode
))
11739 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
11743 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11744 * having the constraint itself; here we also invalidate for relations
11745 * that have any triggers that are part of the constraint.
11747 foreach(lc
, otherrelids
)
11748 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
11750 systable_endscan(scan
);
11752 table_close(tgrel
, RowExclusiveLock
);
11753 table_close(conrel
, RowExclusiveLock
);
11759 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11760 * constraint is altered.
11762 * *otherrelids is appended OIDs of relations containing affected triggers.
11764 * Note that we must recurse even when the values are correct, in case
11765 * indirect descendants have had their constraints altered locally.
11766 * (This could be avoided if we forbade altering constraints in partitions
11767 * but existing releases don't do that.)
11770 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
11771 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
11774 Form_pg_constraint currcon
;
11777 bool changed
= false;
11779 /* since this function recurses, it could be driven to stack overflow */
11780 check_stack_depth();
11782 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11783 conoid
= currcon
->oid
;
11784 refrelid
= currcon
->confrelid
;
11787 * Update pg_constraint with the flags from cmdcon.
11789 * If called to modify a constraint that's already in the desired state,
11790 * silently do nothing.
11792 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11793 currcon
->condeferred
!= cmdcon
->initdeferred
)
11795 HeapTuple copyTuple
;
11796 Form_pg_constraint copy_con
;
11799 SysScanDesc tgscan
;
11801 copyTuple
= heap_copytuple(contuple
);
11802 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11803 copy_con
->condeferrable
= cmdcon
->deferrable
;
11804 copy_con
->condeferred
= cmdcon
->initdeferred
;
11805 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11807 InvokeObjectPostAlterHook(ConstraintRelationId
,
11810 heap_freetuple(copyTuple
);
11813 /* Make new constraint flags visible to others */
11814 CacheInvalidateRelcache(rel
);
11817 * Now we need to update the multiple entries in pg_trigger that
11818 * implement the constraint.
11820 ScanKeyInit(&tgkey
,
11821 Anum_pg_trigger_tgconstraint
,
11822 BTEqualStrategyNumber
, F_OIDEQ
,
11823 ObjectIdGetDatum(conoid
));
11824 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11826 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11828 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11829 Form_pg_trigger copy_tg
;
11830 HeapTuple tgCopyTuple
;
11833 * Remember OIDs of other relation(s) involved in FK constraint.
11834 * (Note: it's likely that we could skip forcing a relcache inval
11835 * for other rels that don't have a trigger whose properties
11836 * change, but let's be conservative.)
11838 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11839 *otherrelids
= list_append_unique_oid(*otherrelids
,
11843 * Update deferrability of RI_FKey_noaction_del,
11844 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11845 * triggers, but not others; see createForeignKeyActionTriggers
11846 * and CreateFKCheckTrigger.
11848 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11849 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11850 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11851 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11854 tgCopyTuple
= heap_copytuple(tgtuple
);
11855 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11857 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11858 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11859 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11861 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11863 heap_freetuple(tgCopyTuple
);
11866 systable_endscan(tgscan
);
11870 * If the table at either end of the constraint is partitioned, we need to
11871 * recurse and handle every constraint that is a child of this one.
11873 * (This assumes that the recurse flag is forcibly set for partitioned
11874 * tables, and not set for legacy inheritance, though we don't check for
11877 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11878 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11882 HeapTuple childtup
;
11885 Anum_pg_constraint_conparentid
,
11886 BTEqualStrategyNumber
, F_OIDEQ
,
11887 ObjectIdGetDatum(conoid
));
11889 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11890 true, NULL
, 1, &pkey
);
11892 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11894 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11897 childrel
= table_open(childcon
->conrelid
, lockmode
);
11898 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11899 otherrelids
, lockmode
);
11900 table_close(childrel
, NoLock
);
11903 systable_endscan(pscan
);
11910 * ALTER TABLE VALIDATE CONSTRAINT
11912 * XXX The reason we handle recursion here rather than at Phase 1 is because
11913 * there's no good way to skip recursing when handling foreign keys: there is
11914 * no need to lock children in that case, yet we wouldn't be able to avoid
11915 * doing so at that level.
11917 * Return value is the address of the validated constraint. If the constraint
11918 * was already validated, InvalidObjectAddress is returned.
11920 static ObjectAddress
11921 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
11922 bool recurse
, bool recursing
, LOCKMODE lockmode
)
11926 ScanKeyData skey
[3];
11928 Form_pg_constraint con
;
11929 ObjectAddress address
;
11931 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11934 * Find and check the target constraint
11936 ScanKeyInit(&skey
[0],
11937 Anum_pg_constraint_conrelid
,
11938 BTEqualStrategyNumber
, F_OIDEQ
,
11939 ObjectIdGetDatum(RelationGetRelid(rel
)));
11940 ScanKeyInit(&skey
[1],
11941 Anum_pg_constraint_contypid
,
11942 BTEqualStrategyNumber
, F_OIDEQ
,
11943 ObjectIdGetDatum(InvalidOid
));
11944 ScanKeyInit(&skey
[2],
11945 Anum_pg_constraint_conname
,
11946 BTEqualStrategyNumber
, F_NAMEEQ
,
11947 CStringGetDatum(constrName
));
11948 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11949 true, NULL
, 3, skey
);
11951 /* There can be at most one matching row */
11952 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
11954 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11955 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11956 constrName
, RelationGetRelationName(rel
))));
11958 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11959 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
11960 con
->contype
!= CONSTRAINT_CHECK
)
11962 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11963 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11964 constrName
, RelationGetRelationName(rel
))));
11966 if (!con
->convalidated
)
11968 AlteredTableInfo
*tab
;
11969 HeapTuple copyTuple
;
11970 Form_pg_constraint copy_con
;
11972 if (con
->contype
== CONSTRAINT_FOREIGN
)
11974 NewConstraint
*newcon
;
11975 Constraint
*fkconstraint
;
11977 /* Queue validation for phase 3 */
11978 fkconstraint
= makeNode(Constraint
);
11979 /* for now this is all we need */
11980 fkconstraint
->conname
= constrName
;
11982 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11983 newcon
->name
= constrName
;
11984 newcon
->contype
= CONSTR_FOREIGN
;
11985 newcon
->refrelid
= con
->confrelid
;
11986 newcon
->refindid
= con
->conindid
;
11987 newcon
->conid
= con
->oid
;
11988 newcon
->qual
= (Node
*) fkconstraint
;
11990 /* Find or create work queue entry for this table */
11991 tab
= ATGetQueueEntry(wqueue
, rel
);
11992 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11995 * We disallow creating invalid foreign keys to or from
11996 * partitioned tables, so ignoring the recursion bit is okay.
11999 else if (con
->contype
== CONSTRAINT_CHECK
)
12001 List
*children
= NIL
;
12003 NewConstraint
*newcon
;
12008 * If we're recursing, the parent has already done this, so skip
12009 * it. Also, if the constraint is a NO INHERIT constraint, we
12010 * shouldn't try to look for it in the children.
12012 if (!recursing
&& !con
->connoinherit
)
12013 children
= find_all_inheritors(RelationGetRelid(rel
),
12017 * For CHECK constraints, we must ensure that we only mark the
12018 * constraint as validated on the parent if it's already validated
12021 * We recurse before validating on the parent, to reduce risk of
12024 foreach(child
, children
)
12026 Oid childoid
= lfirst_oid(child
);
12029 if (childoid
== RelationGetRelid(rel
))
12033 * If we are told not to recurse, there had better not be any
12034 * child tables, because we can't mark the constraint on the
12035 * parent valid unless it is valid for all child tables.
12039 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12040 errmsg("constraint must be validated on child tables too")));
12042 /* find_all_inheritors already got lock */
12043 childrel
= table_open(childoid
, NoLock
);
12045 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
12047 table_close(childrel
, NoLock
);
12050 /* Queue validation for phase 3 */
12051 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
12052 newcon
->name
= constrName
;
12053 newcon
->contype
= CONSTR_CHECK
;
12054 newcon
->refrelid
= InvalidOid
;
12055 newcon
->refindid
= InvalidOid
;
12056 newcon
->conid
= con
->oid
;
12058 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
12059 Anum_pg_constraint_conbin
);
12060 conbin
= TextDatumGetCString(val
);
12061 newcon
->qual
= (Node
*) stringToNode(conbin
);
12063 /* Find or create work queue entry for this table */
12064 tab
= ATGetQueueEntry(wqueue
, rel
);
12065 tab
->constraints
= lappend(tab
->constraints
, newcon
);
12068 * Invalidate relcache so that others see the new validated
12071 CacheInvalidateRelcache(rel
);
12075 * Now update the catalog, while we have the door open.
12077 copyTuple
= heap_copytuple(tuple
);
12078 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
12079 copy_con
->convalidated
= true;
12080 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
12082 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
12084 heap_freetuple(copyTuple
);
12086 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
12089 address
= InvalidObjectAddress
; /* already validated */
12091 systable_endscan(scan
);
12093 table_close(conrel
, RowExclusiveLock
);
12100 * transformColumnNameList - transform list of column names
12102 * Lookup each name and return its attnum and, optionally, type OID
12104 * Note: the name of this function suggests that it's general-purpose,
12105 * but actually it's only used to look up names appearing in foreign-key
12106 * clauses. The error messages would need work to use it in other cases,
12107 * and perhaps the validity checks as well.
12110 transformColumnNameList(Oid relId
, List
*colList
,
12111 int16
*attnums
, Oid
*atttypids
)
12117 foreach(l
, colList
)
12119 char *attname
= strVal(lfirst(l
));
12120 HeapTuple atttuple
;
12121 Form_pg_attribute attform
;
12123 atttuple
= SearchSysCacheAttName(relId
, attname
);
12124 if (!HeapTupleIsValid(atttuple
))
12126 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12127 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12129 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
12130 if (attform
->attnum
< 0)
12132 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12133 errmsg("system columns cannot be used in foreign keys")));
12134 if (attnum
>= INDEX_MAX_KEYS
)
12136 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
12137 errmsg("cannot have more than %d keys in a foreign key",
12139 attnums
[attnum
] = attform
->attnum
;
12140 if (atttypids
!= NULL
)
12141 atttypids
[attnum
] = attform
->atttypid
;
12142 ReleaseSysCache(atttuple
);
12150 * transformFkeyGetPrimaryKey -
12152 * Look up the names, attnums, and types of the primary key attributes
12153 * for the pkrel. Also return the index OID and index opclasses of the
12154 * index supporting the primary key. Also return whether the index has
12155 * WITHOUT OVERLAPS.
12157 * All parameters except pkrel are output parameters. Also, the function
12158 * return value is the number of attributes in the primary key.
12160 * Used when the column list in the REFERENCES specification is omitted.
12163 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
12164 List
**attnamelist
,
12165 int16
*attnums
, Oid
*atttypids
,
12166 Oid
*opclasses
, bool *pk_has_without_overlaps
)
12168 List
*indexoidlist
;
12169 ListCell
*indexoidscan
;
12170 HeapTuple indexTuple
= NULL
;
12171 Form_pg_index indexStruct
= NULL
;
12172 Datum indclassDatum
;
12173 oidvector
*indclass
;
12177 * Get the list of index OIDs for the table from the relcache, and look up
12178 * each one in the pg_index syscache until we find one marked primary key
12179 * (hopefully there isn't more than one such). Insist it's valid, too.
12181 *indexOid
= InvalidOid
;
12183 indexoidlist
= RelationGetIndexList(pkrel
);
12185 foreach(indexoidscan
, indexoidlist
)
12187 Oid indexoid
= lfirst_oid(indexoidscan
);
12189 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12190 if (!HeapTupleIsValid(indexTuple
))
12191 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12192 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12193 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
12196 * Refuse to use a deferrable primary key. This is per SQL spec,
12197 * and there would be a lot of interesting semantic problems if we
12198 * tried to allow it.
12200 if (!indexStruct
->indimmediate
)
12202 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12203 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12204 RelationGetRelationName(pkrel
))));
12206 *indexOid
= indexoid
;
12209 ReleaseSysCache(indexTuple
);
12212 list_free(indexoidlist
);
12215 * Check that we found it
12217 if (!OidIsValid(*indexOid
))
12219 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12220 errmsg("there is no primary key for referenced table \"%s\"",
12221 RelationGetRelationName(pkrel
))));
12223 /* Must get indclass the hard way */
12224 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12225 Anum_pg_index_indclass
);
12226 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12229 * Now build the list of PK attributes from the indkey definition (we
12230 * assume a primary key cannot have expressional elements)
12232 *attnamelist
= NIL
;
12233 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
12235 int pkattno
= indexStruct
->indkey
.values
[i
];
12237 attnums
[i
] = pkattno
;
12238 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
12239 opclasses
[i
] = indclass
->values
[i
];
12240 *attnamelist
= lappend(*attnamelist
,
12241 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
12244 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12246 ReleaseSysCache(indexTuple
);
12252 * transformFkeyCheckAttrs -
12254 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12255 * reference as part of a foreign key constraint.
12257 * Returns the OID of the unique index supporting the constraint and
12258 * populates the caller-provided 'opclasses' array with the opclasses
12259 * associated with the index columns. Also sets whether the index
12260 * uses WITHOUT OVERLAPS.
12262 * Raises an ERROR on validation failure.
12265 transformFkeyCheckAttrs(Relation pkrel
,
12266 int numattrs
, int16
*attnums
,
12267 bool with_period
, Oid
*opclasses
,
12268 bool *pk_has_without_overlaps
)
12270 Oid indexoid
= InvalidOid
;
12271 bool found
= false;
12272 bool found_deferrable
= false;
12273 List
*indexoidlist
;
12274 ListCell
*indexoidscan
;
12279 * Reject duplicate appearances of columns in the referenced-columns list.
12280 * Such a case is forbidden by the SQL standard, and even if we thought it
12281 * useful to allow it, there would be ambiguity about how to match the
12282 * list to unique indexes (in particular, it'd be unclear which index
12283 * opclass goes with which FK column).
12285 for (i
= 0; i
< numattrs
; i
++)
12287 for (j
= i
+ 1; j
< numattrs
; j
++)
12289 if (attnums
[i
] == attnums
[j
])
12291 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12292 errmsg("foreign key referenced-columns list must not contain duplicates")));
12297 * Get the list of index OIDs for the table from the relcache, and look up
12298 * each one in the pg_index syscache, and match unique indexes to the list
12299 * of attnums we are given.
12301 indexoidlist
= RelationGetIndexList(pkrel
);
12303 foreach(indexoidscan
, indexoidlist
)
12305 HeapTuple indexTuple
;
12306 Form_pg_index indexStruct
;
12308 indexoid
= lfirst_oid(indexoidscan
);
12309 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
12310 if (!HeapTupleIsValid(indexTuple
))
12311 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
12312 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
12315 * Must have the right number of columns; must be unique (or if
12316 * temporal then exclusion instead) and not a partial index; forget it
12317 * if there are any expressions, too. Invalid indexes are out as well.
12319 if (indexStruct
->indnkeyatts
== numattrs
&&
12320 (with_period
? indexStruct
->indisexclusion
: indexStruct
->indisunique
) &&
12321 indexStruct
->indisvalid
&&
12322 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
12323 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
12325 Datum indclassDatum
;
12326 oidvector
*indclass
;
12328 /* Must get indclass the hard way */
12329 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
12330 Anum_pg_index_indclass
);
12331 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
12334 * The given attnum list may match the index columns in any order.
12335 * Check for a match, and extract the appropriate opclasses while
12338 * We know that attnums[] is duplicate-free per the test at the
12339 * start of this function, and we checked above that the number of
12340 * index columns agrees, so if we find a match for each attnums[]
12341 * entry then we must have a one-to-one match in some order.
12343 for (i
= 0; i
< numattrs
; i
++)
12346 for (j
= 0; j
< numattrs
; j
++)
12348 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
12350 opclasses
[i
] = indclass
->values
[j
];
12358 /* The last attribute in the index must be the PERIOD FK part */
12359 if (found
&& with_period
)
12361 int16 periodattnum
= attnums
[numattrs
- 1];
12363 found
= (periodattnum
== indexStruct
->indkey
.values
[numattrs
- 1]);
12367 * Refuse to use a deferrable unique/primary key. This is per SQL
12368 * spec, and there would be a lot of interesting semantic problems
12369 * if we tried to allow it.
12371 if (found
&& !indexStruct
->indimmediate
)
12374 * Remember that we found an otherwise matching index, so that
12375 * we can generate a more appropriate error message.
12377 found_deferrable
= true;
12381 /* We need to know whether the index has WITHOUT OVERLAPS */
12383 *pk_has_without_overlaps
= indexStruct
->indisexclusion
;
12385 ReleaseSysCache(indexTuple
);
12392 if (found_deferrable
)
12394 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12395 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12396 RelationGetRelationName(pkrel
))));
12399 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12400 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12401 RelationGetRelationName(pkrel
))));
12404 list_free(indexoidlist
);
12412 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12413 * Caller has equal regard for binary coercibility and for an exact match.
12415 static CoercionPathType
12416 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
12418 CoercionPathType ret
;
12420 if (targetTypeId
== sourceTypeId
)
12422 ret
= COERCION_PATH_RELABELTYPE
;
12423 *funcid
= InvalidOid
;
12427 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
12428 COERCION_IMPLICIT
, funcid
);
12429 if (ret
== COERCION_PATH_NONE
)
12430 /* A previously-relied-upon cast is now gone. */
12431 elog(ERROR
, "could not find cast from %u to %u",
12432 sourceTypeId
, targetTypeId
);
12439 * Permissions checks on the referenced table for ADD FOREIGN KEY
12441 * Note: we have already checked that the user owns the referencing table,
12442 * else we'd have failed much earlier; no additional checks are needed for it.
12445 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
12447 Oid roleid
= GetUserId();
12448 AclResult aclresult
;
12451 /* Okay if we have relation-level REFERENCES permission */
12452 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
12454 if (aclresult
== ACLCHECK_OK
)
12456 /* Else we must have REFERENCES on each column */
12457 for (i
= 0; i
< natts
; i
++)
12459 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
12460 roleid
, ACL_REFERENCES
);
12461 if (aclresult
!= ACLCHECK_OK
)
12462 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
12463 RelationGetRelationName(rel
));
12468 * Scan the existing rows in a table to verify they meet a proposed FK
12471 * Caller must have opened and locked both relations appropriately.
12474 validateForeignKeyConstraint(char *conname
,
12481 TupleTableSlot
*slot
;
12482 TableScanDesc scan
;
12483 Trigger trig
= {0};
12485 MemoryContext oldcxt
;
12486 MemoryContext perTupCxt
;
12489 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
12492 * Build a trigger call structure; we'll need it either way.
12494 trig
.tgoid
= InvalidOid
;
12495 trig
.tgname
= conname
;
12496 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
12497 trig
.tgisinternal
= true;
12498 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
12499 trig
.tgconstrindid
= pkindOid
;
12500 trig
.tgconstraint
= constraintOid
;
12501 trig
.tgdeferrable
= false;
12502 trig
.tginitdeferred
= false;
12503 /* we needn't fill in remaining fields */
12506 * See if we can do it with a single LEFT JOIN query. A false result
12507 * indicates we must proceed with the fire-the-trigger method. We can't do
12508 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12511 if (!hasperiod
&& RI_Initial_Check(&trig
, rel
, pkrel
))
12515 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12516 * if that tuple had just been inserted. If any of those fail, it should
12517 * ereport(ERROR) and that's that.
12519 snapshot
= RegisterSnapshot(GetLatestSnapshot());
12520 slot
= table_slot_create(rel
, NULL
);
12521 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
12523 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
12524 "validateForeignKeyConstraint",
12525 ALLOCSET_SMALL_SIZES
);
12526 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
12528 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
12530 LOCAL_FCINFO(fcinfo
, 0);
12531 TriggerData trigdata
= {0};
12533 CHECK_FOR_INTERRUPTS();
12536 * Make a call to the trigger function
12538 * No parameters are passed, but we do set a context
12540 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
12543 * We assume RI_FKey_check_ins won't look at flinfo...
12545 trigdata
.type
= T_TriggerData
;
12546 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
12547 trigdata
.tg_relation
= rel
;
12548 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
12549 trigdata
.tg_trigslot
= slot
;
12550 trigdata
.tg_trigger
= &trig
;
12552 fcinfo
->context
= (Node
*) &trigdata
;
12554 RI_FKey_check_ins(fcinfo
);
12556 MemoryContextReset(perTupCxt
);
12559 MemoryContextSwitchTo(oldcxt
);
12560 MemoryContextDelete(perTupCxt
);
12561 table_endscan(scan
);
12562 UnregisterSnapshot(snapshot
);
12563 ExecDropSingleTupleTableSlot(slot
);
12567 * CreateFKCheckTrigger
12568 * Creates the insert (on_insert=true) or update "check" trigger that
12569 * implements a given foreign key
12571 * Returns the OID of the so created trigger.
12574 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
12575 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
12578 ObjectAddress trigAddress
;
12579 CreateTrigStmt
*fk_trigger
;
12582 * Note: for a self-referential FK (referencing and referenced tables are
12583 * the same), it is important that the ON UPDATE action fires before the
12584 * CHECK action, since both triggers will fire on the same row during an
12585 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12586 * state of the row. Triggers fire in name order, so we ensure this by
12587 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12588 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12590 fk_trigger
= makeNode(CreateTrigStmt
);
12591 fk_trigger
->replace
= false;
12592 fk_trigger
->isconstraint
= true;
12593 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
12594 fk_trigger
->relation
= NULL
;
12596 /* Either ON INSERT or ON UPDATE */
12599 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
12600 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
12604 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
12605 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12608 fk_trigger
->args
= NIL
;
12609 fk_trigger
->row
= true;
12610 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12611 fk_trigger
->columns
= NIL
;
12612 fk_trigger
->whenClause
= NULL
;
12613 fk_trigger
->transitionRels
= NIL
;
12614 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12615 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12616 fk_trigger
->constrrel
= NULL
;
12618 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
12619 constraintOid
, indexOid
, InvalidOid
,
12620 parentTrigOid
, NULL
, true, false);
12622 /* Make changes-so-far visible */
12623 CommandCounterIncrement();
12625 return trigAddress
.objectId
;
12629 * createForeignKeyActionTriggers
12630 * Create the referenced-side "action" triggers that implement a foreign
12633 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12637 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
12638 Oid constraintOid
, Oid indexOid
,
12639 Oid parentDelTrigger
, Oid parentUpdTrigger
,
12640 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
12642 CreateTrigStmt
*fk_trigger
;
12643 ObjectAddress trigAddress
;
12646 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12647 * DELETE action on the referenced table.
12649 fk_trigger
= makeNode(CreateTrigStmt
);
12650 fk_trigger
->replace
= false;
12651 fk_trigger
->isconstraint
= true;
12652 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12653 fk_trigger
->relation
= NULL
;
12654 fk_trigger
->args
= NIL
;
12655 fk_trigger
->row
= true;
12656 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12657 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
12658 fk_trigger
->columns
= NIL
;
12659 fk_trigger
->whenClause
= NULL
;
12660 fk_trigger
->transitionRels
= NIL
;
12661 fk_trigger
->constrrel
= NULL
;
12663 switch (fkconstraint
->fk_del_action
)
12665 case FKCONSTR_ACTION_NOACTION
:
12666 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12667 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12668 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
12670 case FKCONSTR_ACTION_RESTRICT
:
12671 fk_trigger
->deferrable
= false;
12672 fk_trigger
->initdeferred
= false;
12673 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
12675 case FKCONSTR_ACTION_CASCADE
:
12676 fk_trigger
->deferrable
= false;
12677 fk_trigger
->initdeferred
= false;
12678 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
12680 case FKCONSTR_ACTION_SETNULL
:
12681 fk_trigger
->deferrable
= false;
12682 fk_trigger
->initdeferred
= false;
12683 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
12685 case FKCONSTR_ACTION_SETDEFAULT
:
12686 fk_trigger
->deferrable
= false;
12687 fk_trigger
->initdeferred
= false;
12688 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
12691 elog(ERROR
, "unrecognized FK action type: %d",
12692 (int) fkconstraint
->fk_del_action
);
12696 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12697 RelationGetRelid(rel
),
12698 constraintOid
, indexOid
, InvalidOid
,
12699 parentDelTrigger
, NULL
, true, false);
12701 *deleteTrigOid
= trigAddress
.objectId
;
12703 /* Make changes-so-far visible */
12704 CommandCounterIncrement();
12707 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12708 * UPDATE action on the referenced table.
12710 fk_trigger
= makeNode(CreateTrigStmt
);
12711 fk_trigger
->replace
= false;
12712 fk_trigger
->isconstraint
= true;
12713 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12714 fk_trigger
->relation
= NULL
;
12715 fk_trigger
->args
= NIL
;
12716 fk_trigger
->row
= true;
12717 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12718 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12719 fk_trigger
->columns
= NIL
;
12720 fk_trigger
->whenClause
= NULL
;
12721 fk_trigger
->transitionRels
= NIL
;
12722 fk_trigger
->constrrel
= NULL
;
12724 switch (fkconstraint
->fk_upd_action
)
12726 case FKCONSTR_ACTION_NOACTION
:
12727 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12728 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12729 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
12731 case FKCONSTR_ACTION_RESTRICT
:
12732 fk_trigger
->deferrable
= false;
12733 fk_trigger
->initdeferred
= false;
12734 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
12736 case FKCONSTR_ACTION_CASCADE
:
12737 fk_trigger
->deferrable
= false;
12738 fk_trigger
->initdeferred
= false;
12739 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
12741 case FKCONSTR_ACTION_SETNULL
:
12742 fk_trigger
->deferrable
= false;
12743 fk_trigger
->initdeferred
= false;
12744 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
12746 case FKCONSTR_ACTION_SETDEFAULT
:
12747 fk_trigger
->deferrable
= false;
12748 fk_trigger
->initdeferred
= false;
12749 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
12752 elog(ERROR
, "unrecognized FK action type: %d",
12753 (int) fkconstraint
->fk_upd_action
);
12757 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12758 RelationGetRelid(rel
),
12759 constraintOid
, indexOid
, InvalidOid
,
12760 parentUpdTrigger
, NULL
, true, false);
12762 *updateTrigOid
= trigAddress
.objectId
;
12766 * createForeignKeyCheckTriggers
12767 * Create the referencing-side "check" triggers that implement a foreign
12770 * Returns the OIDs of the so created triggers in *insertTrigOid and
12774 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
12775 Constraint
*fkconstraint
, Oid constraintOid
,
12777 Oid parentInsTrigger
, Oid parentUpdTrigger
,
12778 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
12780 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12781 constraintOid
, indexOid
,
12782 parentInsTrigger
, true);
12783 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12784 constraintOid
, indexOid
,
12785 parentUpdTrigger
, false);
12789 * ALTER TABLE DROP CONSTRAINT
12791 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12794 ATExecDropConstraint(Relation rel
, const char *constrName
,
12795 DropBehavior behavior
, bool recurse
,
12796 bool missing_ok
, LOCKMODE lockmode
)
12800 ScanKeyData skey
[3];
12802 bool found
= false;
12804 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12807 * Find and drop the target constraint
12809 ScanKeyInit(&skey
[0],
12810 Anum_pg_constraint_conrelid
,
12811 BTEqualStrategyNumber
, F_OIDEQ
,
12812 ObjectIdGetDatum(RelationGetRelid(rel
)));
12813 ScanKeyInit(&skey
[1],
12814 Anum_pg_constraint_contypid
,
12815 BTEqualStrategyNumber
, F_OIDEQ
,
12816 ObjectIdGetDatum(InvalidOid
));
12817 ScanKeyInit(&skey
[2],
12818 Anum_pg_constraint_conname
,
12819 BTEqualStrategyNumber
, F_NAMEEQ
,
12820 CStringGetDatum(constrName
));
12821 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12822 true, NULL
, 3, skey
);
12824 /* There can be at most one matching row */
12825 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12827 List
*readyRels
= NIL
;
12829 dropconstraint_internal(rel
, tuple
, behavior
, recurse
, false,
12830 missing_ok
, &readyRels
, lockmode
);
12834 systable_endscan(scan
);
12840 errcode(ERRCODE_UNDEFINED_OBJECT
),
12841 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12842 constrName
, RelationGetRelationName(rel
)));
12845 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12846 constrName
, RelationGetRelationName(rel
)));
12849 table_close(conrel
, RowExclusiveLock
);
12853 * Remove a constraint, using its pg_constraint tuple
12855 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12858 * Returns the address of the constraint being removed.
12860 static ObjectAddress
12861 dropconstraint_internal(Relation rel
, HeapTuple constraintTup
, DropBehavior behavior
,
12862 bool recurse
, bool recursing
, bool missing_ok
, List
**readyRels
,
12866 Form_pg_constraint con
;
12867 ObjectAddress conobj
;
12870 bool is_no_inherit_constraint
= false;
12871 bool dropping_pk
= false;
12873 List
*unconstrained_cols
= NIL
;
12876 if (list_member_oid(*readyRels
, RelationGetRelid(rel
)))
12877 return InvalidObjectAddress
;
12878 *readyRels
= lappend_oid(*readyRels
, RelationGetRelid(rel
));
12880 /* Guard against stack overflow due to overly deep inheritance tree. */
12881 check_stack_depth();
12883 /* At top level, permission check was done in ATPrepCmd, else do it */
12885 ATSimplePermissions(AT_DropConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
12887 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12889 con
= (Form_pg_constraint
) GETSTRUCT(constraintTup
);
12890 constrName
= NameStr(con
->conname
);
12892 /* Don't allow drop of inherited constraints */
12893 if (con
->coninhcount
> 0 && !recursing
)
12895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12896 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12897 constrName
, RelationGetRelationName(rel
))));
12900 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
12901 * more checks and actions below, so obtain the list of columns that are
12902 * constrained by the constraint being dropped.
12904 if (con
->contype
== CONSTRAINT_NOTNULL
)
12906 AttrNumber colnum
= extractNotNullColumn(constraintTup
);
12908 if (colnum
!= InvalidAttrNumber
)
12909 unconstrained_cols
= list_make1_int(colnum
);
12911 else if (con
->contype
== CONSTRAINT_PRIMARY
)
12919 dropping_pk
= true;
12921 adatum
= heap_getattr(constraintTup
, Anum_pg_constraint_conkey
,
12922 RelationGetDescr(conrel
), &isNull
);
12924 elog(ERROR
, "null conkey for constraint %u", con
->oid
);
12925 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
12926 numkeys
= ARR_DIMS(arr
)[0];
12927 if (ARR_NDIM(arr
) != 1 ||
12929 ARR_HASNULL(arr
) ||
12930 ARR_ELEMTYPE(arr
) != INT2OID
)
12931 elog(ERROR
, "conkey is not a 1-D smallint array");
12932 attnums
= (int16
*) ARR_DATA_PTR(arr
);
12934 for (int i
= 0; i
< numkeys
; i
++)
12935 unconstrained_cols
= lappend_int(unconstrained_cols
, attnums
[i
]);
12938 is_no_inherit_constraint
= con
->connoinherit
;
12941 * If it's a foreign-key constraint, we'd better lock the referenced table
12942 * and check that that's not in use, just as we've already done for the
12943 * constrained table (else we might, eg, be dropping a trigger that has
12944 * unfired events). But we can/must skip that in the self-referential
12947 if (con
->contype
== CONSTRAINT_FOREIGN
&&
12948 con
->confrelid
!= RelationGetRelid(rel
))
12952 /* Must match lock taken by RemoveTriggerById: */
12953 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
12954 CheckTableNotInUse(frel
, "ALTER TABLE");
12955 table_close(frel
, NoLock
);
12959 * Perform the actual constraint deletion
12961 ObjectAddressSet(conobj
, ConstraintRelationId
, con
->oid
);
12962 performDeletion(&conobj
, behavior
, 0);
12965 * If this was a NOT NULL or the primary key, the constrained columns must
12966 * have had pg_attribute.attnotnull set. See if we need to reset it, and
12969 if (unconstrained_cols
)
12976 /* Make the above deletion visible */
12977 CommandCounterIncrement();
12979 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
12982 * We want to test columns for their presence in the primary key, but
12983 * only if we're not dropping it.
12985 pkcols
= dropping_pk
? NULL
:
12986 RelationGetIndexAttrBitmap(rel
,
12987 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
12988 ircols
= RelationGetIndexAttrBitmap(rel
, INDEX_ATTR_BITMAP_IDENTITY_KEY
);
12990 foreach(lc
, unconstrained_cols
)
12992 AttrNumber attnum
= lfirst_int(lc
);
12995 Form_pg_attribute attForm
;
12998 * Obtain pg_attribute tuple and verify conditions on it. We use
12999 * a copy we can scribble on.
13001 atttup
= SearchSysCacheCopyAttNum(RelationGetRelid(rel
), attnum
);
13002 if (!HeapTupleIsValid(atttup
))
13003 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
13004 attnum
, RelationGetRelid(rel
));
13005 attForm
= (Form_pg_attribute
) GETSTRUCT(atttup
);
13008 * Since the above deletion has been made visible, we can now
13009 * search for any remaining constraints on this column (or these
13010 * columns, in the case we're dropping a multicol primary key.)
13011 * Then, verify whether any further NOT NULL or primary key
13012 * exists, and reset attnotnull if none.
13014 * However, if this is a generated identity column, abort the
13015 * whole thing with a specific error message, because the
13016 * constraint is required in that case.
13018 contup
= findNotNullConstraintAttnum(RelationGetRelid(rel
), attnum
);
13020 bms_is_member(attnum
- FirstLowInvalidHeapAttributeNumber
,
13025 * It's not valid to drop the not-null constraint for a GENERATED
13026 * AS IDENTITY column.
13028 if (attForm
->attidentity
)
13030 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
13031 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13032 get_attname(RelationGetRelid(rel
), attnum
,
13034 RelationGetRelationName(rel
)));
13037 * It's not valid to drop the not-null constraint for a column in
13038 * the replica identity index, either. (FULL is not affected.)
13040 if (bms_is_member(lfirst_int(lc
) - FirstLowInvalidHeapAttributeNumber
, ircols
))
13042 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13043 errmsg("column \"%s\" is in index used as replica identity",
13044 get_attname(RelationGetRelid(rel
), lfirst_int(lc
), false)));
13046 /* Reset attnotnull */
13047 if (attForm
->attnotnull
)
13049 attForm
->attnotnull
= false;
13050 CatalogTupleUpdate(attrel
, &atttup
->t_self
, atttup
);
13053 table_close(attrel
, RowExclusiveLock
);
13057 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13058 * are dropped via the dependency mechanism, so we're done here.
13060 if (con
->contype
!= CONSTRAINT_CHECK
&&
13061 con
->contype
!= CONSTRAINT_NOTNULL
&&
13062 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
13064 table_close(conrel
, RowExclusiveLock
);
13069 * Propagate to children as appropriate. Unlike most other ALTER
13070 * routines, we have to do this one level of recursion at a time; we can't
13071 * use find_all_inheritors to do it in one pass.
13073 if (!is_no_inherit_constraint
)
13074 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
13079 * For a partitioned table, if partitions exist and we are told not to
13080 * recurse, it's a user error. It doesn't make sense to have a constraint
13081 * be defined only on the parent, especially if it's a partitioned table.
13083 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
13084 children
!= NIL
&& !recurse
)
13086 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13087 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13088 errhint("Do not specify the ONLY keyword.")));
13090 /* For not-null constraints we recurse by column name */
13091 if (con
->contype
== CONSTRAINT_NOTNULL
)
13092 colname
= NameStr(TupleDescAttr(RelationGetDescr(rel
),
13093 linitial_int(unconstrained_cols
) - 1)->attname
);
13095 colname
= NULL
; /* keep compiler quiet */
13097 foreach(child
, children
)
13099 Oid childrelid
= lfirst_oid(child
);
13102 Form_pg_constraint childcon
;
13104 if (list_member_oid(*readyRels
, childrelid
))
13105 continue; /* child already processed */
13107 /* find_inheritance_children already got lock */
13108 childrel
= table_open(childrelid
, NoLock
);
13109 CheckTableNotInUse(childrel
, "ALTER TABLE");
13112 * We search for not-null constraint by column number, and other
13113 * constraints by name.
13115 if (con
->contype
== CONSTRAINT_NOTNULL
)
13117 tuple
= findNotNullConstraint(childrelid
, colname
);
13118 if (!HeapTupleIsValid(tuple
))
13119 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13120 colname
, RelationGetRelid(childrel
));
13125 ScanKeyData skey
[3];
13127 ScanKeyInit(&skey
[0],
13128 Anum_pg_constraint_conrelid
,
13129 BTEqualStrategyNumber
, F_OIDEQ
,
13130 ObjectIdGetDatum(childrelid
));
13131 ScanKeyInit(&skey
[1],
13132 Anum_pg_constraint_contypid
,
13133 BTEqualStrategyNumber
, F_OIDEQ
,
13134 ObjectIdGetDatum(InvalidOid
));
13135 ScanKeyInit(&skey
[2],
13136 Anum_pg_constraint_conname
,
13137 BTEqualStrategyNumber
, F_NAMEEQ
,
13138 CStringGetDatum(constrName
));
13139 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
13140 true, NULL
, 3, skey
);
13141 /* There can only be one, so no need to loop */
13142 tuple
= systable_getnext(scan
);
13143 if (!HeapTupleIsValid(tuple
))
13145 (errcode(ERRCODE_UNDEFINED_OBJECT
),
13146 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13148 RelationGetRelationName(childrel
))));
13149 tuple
= heap_copytuple(tuple
);
13150 systable_endscan(scan
);
13153 childcon
= (Form_pg_constraint
) GETSTRUCT(tuple
);
13155 /* Right now only CHECK and not-null constraints can be inherited */
13156 if (childcon
->contype
!= CONSTRAINT_CHECK
&&
13157 childcon
->contype
!= CONSTRAINT_NOTNULL
)
13158 elog(ERROR
, "inherited constraint is not a CHECK or not-null constraint");
13160 if (childcon
->coninhcount
<= 0) /* shouldn't happen */
13161 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
13162 childrelid
, NameStr(childcon
->conname
));
13167 * If the child constraint has other definition sources, just
13168 * decrement its inheritance count; if not, recurse to delete it.
13170 if (childcon
->coninhcount
== 1 && !childcon
->conislocal
)
13172 /* Time to delete this child constraint, too */
13173 dropconstraint_internal(childrel
, tuple
, behavior
,
13174 recurse
, true, missing_ok
, readyRels
,
13179 /* Child constraint must survive my deletion */
13180 childcon
->coninhcount
--;
13181 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13183 /* Make update visible */
13184 CommandCounterIncrement();
13190 * If we were told to drop ONLY in this table (no recursion) and
13191 * there are no further parents for this constraint, we need to
13192 * mark the inheritors' constraints as locally defined rather than
13195 childcon
->coninhcount
--;
13196 if (childcon
->coninhcount
== 0)
13197 childcon
->conislocal
= true;
13199 CatalogTupleUpdate(conrel
, &tuple
->t_self
, tuple
);
13201 /* Make update visible */
13202 CommandCounterIncrement();
13205 heap_freetuple(tuple
);
13207 table_close(childrel
, NoLock
);
13211 * In addition, when dropping a primary key from a legacy-inheritance
13212 * parent table, we must recurse to children to mark the corresponding NOT
13213 * NULL constraint as no longer inherited, or drop it if this its last
13216 if (con
->contype
== CONSTRAINT_PRIMARY
&&
13217 rel
->rd_rel
->relkind
== RELKIND_RELATION
&&
13218 rel
->rd_rel
->relhassubclass
)
13220 List
*colnames
= NIL
;
13222 List
*pkready
= NIL
;
13225 * Because primary keys are always marked as NO INHERIT, we don't have
13226 * a list of children yet, so obtain one now.
13228 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
13231 * Find out the list of column names to process. Fortunately, we
13232 * already have the list of column numbers.
13234 foreach(lc
, unconstrained_cols
)
13236 colnames
= lappend(colnames
, get_attname(RelationGetRelid(rel
),
13237 lfirst_int(lc
), false));
13240 foreach(child
, children
)
13242 Oid childrelid
= lfirst_oid(child
);
13245 if (list_member_oid(pkready
, childrelid
))
13246 continue; /* child already processed */
13248 /* find_inheritance_children already got lock */
13249 childrel
= table_open(childrelid
, NoLock
);
13250 CheckTableNotInUse(childrel
, "ALTER TABLE");
13252 foreach(lc
, colnames
)
13255 char *colName
= lfirst(lc
);
13257 contup
= findNotNullConstraint(childrelid
, colName
);
13258 if (contup
== NULL
)
13259 elog(ERROR
, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13260 colName
, RelationGetRelationName(childrel
));
13262 dropconstraint_internal(childrel
, contup
,
13263 DROP_RESTRICT
, true, true,
13269 table_close(childrel
, NoLock
);
13271 pkready
= lappend_oid(pkready
, childrelid
);
13275 table_close(conrel
, RowExclusiveLock
);
13281 * ALTER COLUMN TYPE
13283 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13284 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13285 * transformed (and must be, because we rely on some transformed fields).
13287 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13288 * table will be done "in parallel" during phase 3, so all the USING
13289 * expressions should be parsed assuming the original column types. Also,
13290 * this allows a USING expression to refer to a field that will be dropped.
13292 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13293 * the first two execution steps in phase 2; they must not see the effects
13294 * of any other subcommand types, since the USING expressions are parsed
13295 * against the unmodified table's state.
13298 ATPrepAlterColumnType(List
**wqueue
,
13299 AlteredTableInfo
*tab
, Relation rel
,
13300 bool recurse
, bool recursing
,
13301 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
13302 AlterTableUtilityContext
*context
)
13304 char *colName
= cmd
->name
;
13305 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13306 TypeName
*typeName
= def
->typeName
;
13307 Node
*transform
= def
->cooked_default
;
13309 Form_pg_attribute attTup
;
13312 int32 targettypmod
;
13314 NewColumnValue
*newval
;
13315 ParseState
*pstate
= make_parsestate(NULL
);
13316 AclResult aclresult
;
13319 if (rel
->rd_rel
->reloftype
&& !recursing
)
13321 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13322 errmsg("cannot alter column type of typed table")));
13324 /* lookup the attribute so we can check inheritance status */
13325 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
13326 if (!HeapTupleIsValid(tuple
))
13328 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13329 errmsg("column \"%s\" of relation \"%s\" does not exist",
13330 colName
, RelationGetRelationName(rel
))));
13331 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
13332 attnum
= attTup
->attnum
;
13334 /* Can't alter a system attribute */
13337 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13338 errmsg("cannot alter system column \"%s\"",
13342 * Don't alter inherited columns. At outer level, there had better not be
13343 * any inherited definition; when recursing, we assume this was checked at
13344 * the parent level (see below).
13346 if (attTup
->attinhcount
> 0 && !recursing
)
13348 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13349 errmsg("cannot alter inherited column \"%s\"",
13352 /* Don't alter columns used in the partition key */
13353 if (has_partition_attrs(rel
,
13354 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
13357 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13358 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13359 colName
, RelationGetRelationName(rel
))));
13361 /* Look up the target type */
13362 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
13364 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
13365 if (aclresult
!= ACLCHECK_OK
)
13366 aclcheck_error_type(aclresult
, targettype
);
13368 /* And the collation */
13369 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13371 /* make sure datatype is legal for a column */
13372 CheckAttributeType(colName
, targettype
, targetcollid
,
13373 list_make1_oid(rel
->rd_rel
->reltype
),
13376 if (tab
->relkind
== RELKIND_RELATION
||
13377 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
13380 * Set up an expression to transform the old data value to the new
13381 * type. If a USING option was given, use the expression as
13382 * transformed by transformAlterTableStmt, else just take the old
13383 * value and try to coerce it. We do this first so that type
13384 * incompatibility can be detected before we waste effort, and because
13385 * we need the expression to be parsed against the original table row
13390 transform
= (Node
*) makeVar(1, attnum
,
13391 attTup
->atttypid
, attTup
->atttypmod
,
13392 attTup
->attcollation
,
13396 transform
= coerce_to_target_type(pstate
,
13397 transform
, exprType(transform
),
13398 targettype
, targettypmod
,
13399 COERCION_ASSIGNMENT
,
13400 COERCE_IMPLICIT_CAST
,
13402 if (transform
== NULL
)
13404 /* error text depends on whether USING was specified or not */
13405 if (def
->cooked_default
!= NULL
)
13407 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13408 errmsg("result of USING clause for column \"%s\""
13409 " cannot be cast automatically to type %s",
13410 colName
, format_type_be(targettype
)),
13411 errhint("You might need to add an explicit cast.")));
13414 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13415 errmsg("column \"%s\" cannot be cast automatically to type %s",
13416 colName
, format_type_be(targettype
)),
13417 /* translator: USING is SQL, don't translate it */
13418 errhint("You might need to specify \"USING %s::%s\".",
13419 quote_identifier(colName
),
13420 format_type_with_typemod(targettype
,
13424 /* Fix collations after all else */
13425 assign_expr_collations(pstate
, transform
);
13427 /* Plan the expr now so we can accurately assess the need to rewrite. */
13428 transform
= (Node
*) expression_planner((Expr
*) transform
);
13431 * Add a work queue item to make ATRewriteTable update the column
13434 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
13435 newval
->attnum
= attnum
;
13436 newval
->expr
= (Expr
*) transform
;
13437 newval
->is_generated
= false;
13439 tab
->newvals
= lappend(tab
->newvals
, newval
);
13440 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
13441 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
13443 else if (transform
)
13445 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
13446 errmsg("\"%s\" is not a table",
13447 RelationGetRelationName(rel
))));
13449 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
13452 * For relations without storage, do this check now. Regular tables
13453 * will check it later when the table is being rewritten.
13455 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
13458 ReleaseSysCache(tuple
);
13461 * Recurse manually by queueing a new command for each child, if
13462 * necessary. We cannot apply ATSimpleRecursion here because we need to
13463 * remap attribute numbers in the USING expression, if any.
13465 * If we are told not to recurse, there had better not be any child
13466 * tables; else the alter would put them out of step.
13470 Oid relid
= RelationGetRelid(rel
);
13476 child_oids
= find_all_inheritors(relid
, lockmode
,
13477 &child_numparents
);
13480 * find_all_inheritors does the recursive search of the inheritance
13481 * hierarchy, so all we have to do is process all of the relids in the
13482 * list that it returns.
13484 forboth(lo
, child_oids
, li
, child_numparents
)
13486 Oid childrelid
= lfirst_oid(lo
);
13487 int numparents
= lfirst_int(li
);
13489 HeapTuple childtuple
;
13490 Form_pg_attribute childattTup
;
13492 if (childrelid
== relid
)
13495 /* find_all_inheritors already got lock */
13496 childrel
= relation_open(childrelid
, NoLock
);
13497 CheckTableNotInUse(childrel
, "ALTER TABLE");
13500 * Verify that the child doesn't have any inherited definitions of
13501 * this column that came from outside this inheritance hierarchy.
13502 * (renameatt makes a similar test, though in a different way
13503 * because of its different recursion mechanism.)
13505 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
13507 if (!HeapTupleIsValid(childtuple
))
13509 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13510 errmsg("column \"%s\" of relation \"%s\" does not exist",
13511 colName
, RelationGetRelationName(childrel
))));
13512 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
13514 if (childattTup
->attinhcount
> numparents
)
13516 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13517 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13518 colName
, RelationGetRelationName(childrel
))));
13520 ReleaseSysCache(childtuple
);
13523 * Remap the attribute numbers. If no USING expression was
13524 * specified, there is no need for this step.
13526 if (def
->cooked_default
)
13529 bool found_whole_row
;
13531 /* create a copy to scribble on */
13532 cmd
= copyObject(cmd
);
13534 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
13535 RelationGetDescr(rel
),
13537 ((ColumnDef
*) cmd
->def
)->cooked_default
=
13538 map_variable_attnos(def
->cooked_default
,
13541 InvalidOid
, &found_whole_row
);
13542 if (found_whole_row
)
13544 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13545 errmsg("cannot convert whole-row table reference"),
13546 errdetail("USING expression contains a whole-row table reference.")));
13549 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
13550 relation_close(childrel
, NoLock
);
13553 else if (!recursing
&&
13554 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
13556 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
13557 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13560 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
13561 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
13565 * When the data type of a column is changed, a rewrite might not be required
13566 * if the new type is sufficiently identical to the old one, and the USING
13567 * clause isn't trying to insert some other value. It's safe to skip the
13568 * rewrite in these cases:
13570 * - the old type is binary coercible to the new type
13571 * - the new type is an unconstrained domain over the old type
13572 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13574 * In the case of a constrained domain, we could get by with scanning the
13575 * table and checking the constraint rather than actually rewriting it, but we
13576 * don't currently try to do that.
13579 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
13581 Assert(expr
!= NULL
);
13585 /* only one varno, so no need to check that */
13586 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
13588 else if (IsA(expr
, RelabelType
))
13589 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
13590 else if (IsA(expr
, CoerceToDomain
))
13592 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
13594 if (DomainHasConstraints(d
->resulttype
))
13596 expr
= (Node
*) d
->arg
;
13598 else if (IsA(expr
, FuncExpr
))
13600 FuncExpr
*f
= (FuncExpr
*) expr
;
13604 case F_TIMESTAMPTZ_TIMESTAMP
:
13605 case F_TIMESTAMP_TIMESTAMPTZ
:
13606 if (TimestampTimestampTzRequiresRewrite())
13609 expr
= linitial(f
->args
);
13621 * ALTER COLUMN .. SET DATA TYPE
13623 * Return the address of the modified column.
13625 static ObjectAddress
13626 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
13627 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
13629 char *colName
= cmd
->name
;
13630 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13631 TypeName
*typeName
= def
->typeName
;
13633 Form_pg_attribute attTup
,
13636 HeapTuple typeTuple
;
13637 Form_pg_type tform
;
13639 int32 targettypmod
;
13642 Relation attrelation
;
13644 ScanKeyData key
[3];
13647 ObjectAddress address
;
13650 * Clear all the missing values if we're rewriting the table, since this
13651 * renders them pointless.
13657 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
13658 RelationClearMissing(newrel
);
13659 relation_close(newrel
, NoLock
);
13660 /* make sure we don't conflict with later attribute modifications */
13661 CommandCounterIncrement();
13664 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
13666 /* Look up the target column */
13667 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
13668 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
13670 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13671 errmsg("column \"%s\" of relation \"%s\" does not exist",
13672 colName
, RelationGetRelationName(rel
))));
13673 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13674 attnum
= attTup
->attnum
;
13675 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
13677 /* Check for multiple ALTER TYPE on same column --- can't cope */
13678 if (attTup
->atttypid
!= attOldTup
->atttypid
||
13679 attTup
->atttypmod
!= attOldTup
->atttypmod
)
13681 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13682 errmsg("cannot alter type of column \"%s\" twice",
13685 /* Look up the target type (should not fail, since prep found it) */
13686 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
13687 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
13688 targettype
= tform
->oid
;
13689 /* And the collation */
13690 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13693 * If there is a default expression for the column, get it and ensure we
13694 * can coerce it to the new datatype. (We must do this before changing
13695 * the column type, because build_column_default itself will try to
13696 * coerce, and will not issue the error message we want if it fails.)
13698 * We remove any implicit coercion steps at the top level of the old
13699 * default expression; this has been agreed to satisfy the principle of
13700 * least surprise. (The conversion to the new column type should act like
13701 * it started from what the user sees as the stored expression, and the
13702 * implicit coercions aren't going to be shown.)
13704 if (attTup
->atthasdef
)
13706 defaultexpr
= build_column_default(rel
, attnum
);
13707 Assert(defaultexpr
);
13708 defaultexpr
= strip_implicit_coercions(defaultexpr
);
13709 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
13710 defaultexpr
, exprType(defaultexpr
),
13711 targettype
, targettypmod
,
13712 COERCION_ASSIGNMENT
,
13713 COERCE_IMPLICIT_CAST
,
13715 if (defaultexpr
== NULL
)
13717 if (attTup
->attgenerated
)
13719 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13720 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13721 colName
, format_type_be(targettype
))));
13724 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13725 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13726 colName
, format_type_be(targettype
))));
13730 defaultexpr
= NULL
;
13733 * Find everything that depends on the column (constraints, indexes, etc),
13734 * and record enough information to let us recreate the objects.
13736 * The actual recreation does not happen here, but only after we have
13737 * performed all the individual ALTER TYPE operations. We have to save
13738 * the info before executing ALTER TYPE, though, else the deparser will
13741 RememberAllDependentForRebuilding(tab
, AT_AlterColumnType
, rel
, attnum
, colName
);
13744 * Now scan for dependencies of this column on other things. The only
13745 * things we should find are the dependency on the column datatype and
13746 * possibly a collation dependency. Those can be removed.
13748 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13750 ScanKeyInit(&key
[0],
13751 Anum_pg_depend_classid
,
13752 BTEqualStrategyNumber
, F_OIDEQ
,
13753 ObjectIdGetDatum(RelationRelationId
));
13754 ScanKeyInit(&key
[1],
13755 Anum_pg_depend_objid
,
13756 BTEqualStrategyNumber
, F_OIDEQ
,
13757 ObjectIdGetDatum(RelationGetRelid(rel
)));
13758 ScanKeyInit(&key
[2],
13759 Anum_pg_depend_objsubid
,
13760 BTEqualStrategyNumber
, F_INT4EQ
,
13761 Int32GetDatum((int32
) attnum
));
13763 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
13766 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13768 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13769 ObjectAddress foundObject
;
13771 foundObject
.classId
= foundDep
->refclassid
;
13772 foundObject
.objectId
= foundDep
->refobjid
;
13773 foundObject
.objectSubId
= foundDep
->refobjsubid
;
13775 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
13776 elog(ERROR
, "found unexpected dependency type '%c'",
13777 foundDep
->deptype
);
13778 if (!(foundDep
->refclassid
== TypeRelationId
&&
13779 foundDep
->refobjid
== attTup
->atttypid
) &&
13780 !(foundDep
->refclassid
== CollationRelationId
&&
13781 foundDep
->refobjid
== attTup
->attcollation
))
13782 elog(ERROR
, "found unexpected dependency for column: %s",
13783 getObjectDescription(&foundObject
, false));
13785 CatalogTupleDelete(depRel
, &depTup
->t_self
);
13788 systable_endscan(scan
);
13790 table_close(depRel
, RowExclusiveLock
);
13793 * Here we go --- change the recorded column type and collation. (Note
13794 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13795 * fix up the missing value if any.
13797 if (attTup
->atthasmissing
)
13802 /* if rewrite is true the missing value should already be cleared */
13803 Assert(tab
->rewrite
== 0);
13805 /* Get the missing value datum */
13806 missingval
= heap_getattr(heapTup
,
13807 Anum_pg_attribute_attmissingval
,
13808 attrelation
->rd_att
,
13811 /* if it's a null array there is nothing to do */
13816 * Get the datum out of the array and repack it in a new array
13817 * built with the new type data. We assume that since the table
13818 * doesn't need rewriting, the actual Datum doesn't need to be
13819 * changed, only the array metadata.
13824 Datum valuesAtt
[Natts_pg_attribute
] = {0};
13825 bool nullsAtt
[Natts_pg_attribute
] = {0};
13826 bool replacesAtt
[Natts_pg_attribute
] = {0};
13829 missingval
= array_get_element(missingval
,
13837 missingval
= PointerGetDatum(construct_array(&missingval
,
13844 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
13845 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
13846 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
13848 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
13849 valuesAtt
, nullsAtt
, replacesAtt
);
13850 heap_freetuple(heapTup
);
13852 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13856 attTup
->atttypid
= targettype
;
13857 attTup
->atttypmod
= targettypmod
;
13858 attTup
->attcollation
= targetcollid
;
13859 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
13861 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
13862 errmsg("too many array dimensions"));
13863 attTup
->attndims
= list_length(typeName
->arrayBounds
);
13864 attTup
->attlen
= tform
->typlen
;
13865 attTup
->attbyval
= tform
->typbyval
;
13866 attTup
->attalign
= tform
->typalign
;
13867 attTup
->attstorage
= tform
->typstorage
;
13868 attTup
->attcompression
= InvalidCompressionMethod
;
13870 ReleaseSysCache(typeTuple
);
13872 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
13874 table_close(attrelation
, RowExclusiveLock
);
13876 /* Install dependencies on new datatype and collation */
13877 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
13878 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
13881 * Drop any pg_statistic entry for the column, since it's now wrong type
13883 RemoveStatistics(RelationGetRelid(rel
), attnum
);
13885 InvokeObjectPostAlterHook(RelationRelationId
,
13886 RelationGetRelid(rel
), attnum
);
13889 * Update the default, if present, by brute force --- remove and re-add
13890 * the default. Probably unsafe to take shortcuts, since the new version
13891 * may well have additional dependencies. (It's okay to do this now,
13892 * rather than after other ALTER TYPE commands, since the default won't
13893 * depend on other column types.)
13898 * If it's a GENERATED default, drop its dependency records, in
13899 * particular its INTERNAL dependency on the column, which would
13900 * otherwise cause dependency.c to refuse to perform the deletion.
13902 if (attTup
->attgenerated
)
13904 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
13906 if (!OidIsValid(attrdefoid
))
13907 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
13908 RelationGetRelid(rel
), attnum
);
13909 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
13913 * Make updates-so-far visible, particularly the new pg_attribute row
13914 * which will be updated again.
13916 CommandCounterIncrement();
13919 * We use RESTRICT here for safety, but at present we do not expect
13920 * anything to depend on the default.
13922 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
13925 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
13928 ObjectAddressSubSet(address
, RelationRelationId
,
13929 RelationGetRelid(rel
), attnum
);
13932 heap_freetuple(heapTup
);
13938 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13939 * that depends on the column (constraints, indexes, etc), and record enough
13940 * information to let us recreate the objects.
13943 RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
13944 Relation rel
, AttrNumber attnum
, const char *colName
)
13947 ScanKeyData key
[3];
13951 Assert(subtype
== AT_AlterColumnType
|| subtype
== AT_SetExpression
);
13953 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13955 ScanKeyInit(&key
[0],
13956 Anum_pg_depend_refclassid
,
13957 BTEqualStrategyNumber
, F_OIDEQ
,
13958 ObjectIdGetDatum(RelationRelationId
));
13959 ScanKeyInit(&key
[1],
13960 Anum_pg_depend_refobjid
,
13961 BTEqualStrategyNumber
, F_OIDEQ
,
13962 ObjectIdGetDatum(RelationGetRelid(rel
)));
13963 ScanKeyInit(&key
[2],
13964 Anum_pg_depend_refobjsubid
,
13965 BTEqualStrategyNumber
, F_INT4EQ
,
13966 Int32GetDatum((int32
) attnum
));
13968 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
13971 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13973 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13974 ObjectAddress foundObject
;
13976 foundObject
.classId
= foundDep
->classid
;
13977 foundObject
.objectId
= foundDep
->objid
;
13978 foundObject
.objectSubId
= foundDep
->objsubid
;
13980 switch (getObjectClass(&foundObject
))
13984 char relKind
= get_rel_relkind(foundObject
.objectId
);
13986 if (relKind
== RELKIND_INDEX
||
13987 relKind
== RELKIND_PARTITIONED_INDEX
)
13989 Assert(foundObject
.objectSubId
== 0);
13990 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
13992 else if (relKind
== RELKIND_SEQUENCE
)
13995 * This must be a SERIAL column's sequence. We need
13996 * not do anything to it.
13998 Assert(foundObject
.objectSubId
== 0);
14002 /* Not expecting any other direct dependencies... */
14003 elog(ERROR
, "unexpected object depending on column: %s",
14004 getObjectDescription(&foundObject
, false));
14009 case OCLASS_CONSTRAINT
:
14010 Assert(foundObject
.objectSubId
== 0);
14011 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
14014 case OCLASS_REWRITE
:
14015 /* XXX someday see if we can cope with revising views */
14016 if (subtype
== AT_AlterColumnType
)
14018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14019 errmsg("cannot alter type of a column used by a view or rule"),
14020 errdetail("%s depends on column \"%s\"",
14021 getObjectDescription(&foundObject
, false),
14025 case OCLASS_TRIGGER
:
14028 * A trigger can depend on a column because the column is
14029 * specified as an update target, or because the column is
14030 * used in the trigger's WHEN condition. The first case would
14031 * not require any extra work, but the second case would
14032 * require updating the WHEN expression, which will take a
14033 * significant amount of new code. Since we can't easily tell
14034 * which case applies, we punt for both. FIXME someday.
14036 if (subtype
== AT_AlterColumnType
)
14038 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14039 errmsg("cannot alter type of a column used in a trigger definition"),
14040 errdetail("%s depends on column \"%s\"",
14041 getObjectDescription(&foundObject
, false),
14045 case OCLASS_POLICY
:
14048 * A policy can depend on a column because the column is
14049 * specified in the policy's USING or WITH CHECK qual
14050 * expressions. It might be possible to rewrite and recheck
14051 * the policy expression, but punt for now. It's certainly
14052 * easy enough to remove and recreate the policy; still, FIXME
14055 if (subtype
== AT_AlterColumnType
)
14057 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14058 errmsg("cannot alter type of a column used in a policy definition"),
14059 errdetail("%s depends on column \"%s\"",
14060 getObjectDescription(&foundObject
, false),
14064 case OCLASS_DEFAULT
:
14066 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
14068 if (col
.objectId
== RelationGetRelid(rel
) &&
14069 col
.objectSubId
== attnum
)
14072 * Ignore the column's own default expression. The
14073 * caller deals with it.
14079 * This must be a reference from the expression of a
14080 * generated column elsewhere in the same table.
14081 * Changing the type/generated expression of a column
14082 * that is used by a generated column is not allowed
14083 * by SQL standard, so just punt for now. It might be
14084 * doable with some thinking and effort.
14086 if (subtype
== AT_AlterColumnType
)
14088 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14089 errmsg("cannot alter type of a column used by a generated column"),
14090 errdetail("Column \"%s\" is used by generated column \"%s\".",
14092 get_attname(col
.objectId
,
14099 case OCLASS_STATISTIC_EXT
:
14102 * Give the extended-stats machinery a chance to fix anything
14103 * that this column type change would break.
14105 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
14111 case OCLASS_COLLATION
:
14112 case OCLASS_CONVERSION
:
14113 case OCLASS_LANGUAGE
:
14114 case OCLASS_LARGEOBJECT
:
14115 case OCLASS_OPERATOR
:
14116 case OCLASS_OPCLASS
:
14117 case OCLASS_OPFAMILY
:
14120 case OCLASS_AMPROC
:
14121 case OCLASS_SCHEMA
:
14122 case OCLASS_TSPARSER
:
14123 case OCLASS_TSDICT
:
14124 case OCLASS_TSTEMPLATE
:
14125 case OCLASS_TSCONFIG
:
14127 case OCLASS_ROLE_MEMBERSHIP
:
14128 case OCLASS_DATABASE
:
14129 case OCLASS_TBLSPACE
:
14131 case OCLASS_FOREIGN_SERVER
:
14132 case OCLASS_USER_MAPPING
:
14133 case OCLASS_DEFACL
:
14134 case OCLASS_EXTENSION
:
14135 case OCLASS_EVENT_TRIGGER
:
14136 case OCLASS_PARAMETER_ACL
:
14137 case OCLASS_PUBLICATION
:
14138 case OCLASS_PUBLICATION_NAMESPACE
:
14139 case OCLASS_PUBLICATION_REL
:
14140 case OCLASS_SUBSCRIPTION
:
14141 case OCLASS_TRANSFORM
:
14144 * We don't expect any of these sorts of objects to depend on
14147 elog(ERROR
, "unexpected object depending on column: %s",
14148 getObjectDescription(&foundObject
, false));
14152 * There's intentionally no default: case here; we want the
14153 * compiler to warn if a new OCLASS hasn't been handled above.
14158 systable_endscan(scan
);
14159 table_close(depRel
, NoLock
);
14163 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14164 * needs to be reset.
14167 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14169 if (!get_index_isreplident(indoid
))
14172 if (tab
->replicaIdentityIndex
)
14173 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
14175 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
14179 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14182 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14184 if (!get_index_isclustered(indoid
))
14187 if (tab
->clusterOnIndex
)
14188 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
14190 tab
->clusterOnIndex
= get_rel_name(indoid
);
14194 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14195 * to be rebuilt (which we might already know).
14198 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
14201 * This de-duplication check is critical for two independent reasons: we
14202 * mustn't try to recreate the same constraint twice, and if a constraint
14203 * depends on more than one column whose type is to be altered, we must
14204 * capture its definition string before applying any of the column type
14205 * changes. ruleutils.c will get confused if we ask again later.
14207 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
14209 /* OK, capture the constraint's existing definition string */
14210 char *defstring
= pg_get_constraintdef_command(conoid
);
14213 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
14215 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
14219 * For the index of a constraint, if any, remember if it is used for
14220 * the table's replica identity or if it is a clustered index, so that
14221 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14222 * those properties.
14224 indoid
= get_constraint_index(conoid
);
14225 if (OidIsValid(indoid
))
14227 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14228 RememberClusterOnForRebuilding(indoid
, tab
);
14234 * Subroutine for ATExecAlterColumnType: remember that an index needs
14235 * to be rebuilt (which we might already know).
14238 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
14241 * This de-duplication check is critical for two independent reasons: we
14242 * mustn't try to recreate the same index twice, and if an index depends
14243 * on more than one column whose type is to be altered, we must capture
14244 * its definition string before applying any of the column type changes.
14245 * ruleutils.c will get confused if we ask again later.
14247 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
14250 * Before adding it as an index-to-rebuild, we'd better see if it
14251 * belongs to a constraint, and if so rebuild the constraint instead.
14252 * Typically this check fails, because constraint indexes normally
14253 * have only dependencies on their constraint. But it's possible for
14254 * such an index to also have direct dependencies on table columns,
14255 * for example with a partial exclusion constraint.
14257 Oid conoid
= get_index_constraint(indoid
);
14259 if (OidIsValid(conoid
))
14261 RememberConstraintForRebuilding(conoid
, tab
);
14265 /* OK, capture the index's existing definition string */
14266 char *defstring
= pg_get_indexdef_string(indoid
);
14268 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
14270 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
14274 * Remember if this index is used for the table's replica identity
14275 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14276 * can queue up commands necessary to restore those properties.
14278 RememberReplicaIdentityForRebuilding(indoid
, tab
);
14279 RememberClusterOnForRebuilding(indoid
, tab
);
14285 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14286 * needs to be rebuilt (which we might already know).
14289 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
14292 * This de-duplication check is critical for two independent reasons: we
14293 * mustn't try to recreate the same statistics object twice, and if the
14294 * statistics object depends on more than one column whose type is to be
14295 * altered, we must capture its definition string before applying any of
14296 * the type changes. ruleutils.c will get confused if we ask again later.
14298 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
14300 /* OK, capture the statistics object's existing definition string */
14301 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
14303 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
14305 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
14311 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14312 * operations for a particular relation. We have to drop and recreate all the
14313 * indexes and constraints that depend on the altered columns. We do the
14314 * actual dropping here, but re-creation is managed by adding work queue
14315 * entries to do those steps later.
14318 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
14321 ObjectAddresses
*objects
;
14322 ListCell
*def_item
;
14323 ListCell
*oid_item
;
14326 * Collect all the constraints and indexes to drop so we can process them
14327 * in a single call. That way we don't have to worry about dependencies
14330 objects
= new_object_addresses();
14333 * Re-parse the index and constraint definitions, and attach them to the
14334 * appropriate work queue entries. We do this before dropping because in
14335 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14336 * lock on the table the constraint is attached to, and we need to get
14337 * that before reparsing/dropping.
14339 * We can't rely on the output of deparsing to tell us which relation to
14340 * operate on, because concurrent activity might have made the name
14341 * resolve differently. Instead, we've got to use the OID of the
14342 * constraint or index we're processing to figure out which relation to
14345 forboth(oid_item
, tab
->changedConstraintOids
,
14346 def_item
, tab
->changedConstraintDefs
)
14348 Oid oldId
= lfirst_oid(oid_item
);
14350 Form_pg_constraint con
;
14356 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14357 if (!HeapTupleIsValid(tup
)) /* should not happen */
14358 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14359 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
14360 if (OidIsValid(con
->conrelid
))
14361 relid
= con
->conrelid
;
14364 /* must be a domain constraint */
14365 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
14366 if (!OidIsValid(relid
))
14367 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
14369 confrelid
= con
->confrelid
;
14370 contype
= con
->contype
;
14371 conislocal
= con
->conislocal
;
14372 ReleaseSysCache(tup
);
14374 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
14375 add_exact_object_address(&obj
, objects
);
14378 * If the constraint is inherited (only), we don't want to inject a
14379 * new definition here; it'll get recreated when
14380 * ATAddCheckNNConstraint recurses from adding the parent table's
14381 * constraint. But we had to carry the info this far so that we can
14382 * drop the constraint below.
14388 * When rebuilding an FK constraint that references the table we're
14389 * modifying, we might not yet have any lock on the FK's table, so get
14390 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14391 * step, so there's no value in asking for anything weaker.
14393 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
14394 LockRelationOid(relid
, AccessExclusiveLock
);
14396 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
14397 (char *) lfirst(def_item
),
14398 wqueue
, lockmode
, tab
->rewrite
);
14400 forboth(oid_item
, tab
->changedIndexOids
,
14401 def_item
, tab
->changedIndexDefs
)
14403 Oid oldId
= lfirst_oid(oid_item
);
14406 relid
= IndexGetRelation(oldId
, false);
14407 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14408 (char *) lfirst(def_item
),
14409 wqueue
, lockmode
, tab
->rewrite
);
14411 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
14412 add_exact_object_address(&obj
, objects
);
14415 /* add dependencies for new statistics */
14416 forboth(oid_item
, tab
->changedStatisticsOids
,
14417 def_item
, tab
->changedStatisticsDefs
)
14419 Oid oldId
= lfirst_oid(oid_item
);
14422 relid
= StatisticsGetRelation(oldId
, false);
14423 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
14424 (char *) lfirst(def_item
),
14425 wqueue
, lockmode
, tab
->rewrite
);
14427 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
14428 add_exact_object_address(&obj
, objects
);
14432 * Queue up command to restore replica identity index marking
14434 if (tab
->replicaIdentityIndex
)
14436 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14437 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
14439 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
14440 subcmd
->name
= tab
->replicaIdentityIndex
;
14441 cmd
->subtype
= AT_ReplicaIdentity
;
14442 cmd
->def
= (Node
*) subcmd
;
14444 /* do it after indexes and constraints */
14445 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14446 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14450 * Queue up command to restore marking of index used for cluster.
14452 if (tab
->clusterOnIndex
)
14454 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14456 cmd
->subtype
= AT_ClusterOn
;
14457 cmd
->name
= tab
->clusterOnIndex
;
14459 /* do it after indexes and constraints */
14460 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14461 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14465 * It should be okay to use DROP_RESTRICT here, since nothing else should
14466 * be depending on these objects.
14468 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
14470 free_object_addresses(objects
);
14473 * The objects will get recreated during subsequent passes over the work
14479 * Parse the previously-saved definition string for a constraint, index or
14480 * statistics object against the newly-established column data type(s), and
14481 * queue up the resulting command parsetrees for execution.
14483 * This might fail if, for example, you have a WHERE clause that uses an
14484 * operator that's not available for the new column type.
14487 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
14488 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
14490 List
*raw_parsetree_list
;
14491 List
*querytree_list
;
14492 ListCell
*list_item
;
14496 * We expect that we will get only ALTER TABLE and CREATE INDEX
14497 * statements. Hence, there is no need to pass them through
14498 * parse_analyze_*() or the rewriter, but instead we need to pass them
14499 * through parse_utilcmd.c to make them ready for execution.
14501 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
14502 querytree_list
= NIL
;
14503 foreach(list_item
, raw_parsetree_list
)
14505 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
14506 Node
*stmt
= rs
->stmt
;
14508 if (IsA(stmt
, IndexStmt
))
14509 querytree_list
= lappend(querytree_list
,
14510 transformIndexStmt(oldRelId
,
14511 (IndexStmt
*) stmt
,
14513 else if (IsA(stmt
, AlterTableStmt
))
14518 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
14519 (AlterTableStmt
*) stmt
,
14523 querytree_list
= list_concat(querytree_list
, beforeStmts
);
14524 querytree_list
= lappend(querytree_list
, stmt
);
14525 querytree_list
= list_concat(querytree_list
, afterStmts
);
14527 else if (IsA(stmt
, CreateStatsStmt
))
14528 querytree_list
= lappend(querytree_list
,
14529 transformStatsStmt(oldRelId
,
14530 (CreateStatsStmt
*) stmt
,
14533 querytree_list
= lappend(querytree_list
, stmt
);
14536 /* Caller should already have acquired whatever lock we need. */
14537 rel
= relation_open(oldRelId
, NoLock
);
14540 * Attach each generated command to the proper place in the work queue.
14541 * Note this could result in creation of entirely new work-queue entries.
14543 * Also note that we have to tweak the command subtypes, because it turns
14544 * out that re-creation of indexes and constraints has to act a bit
14545 * differently from initial creation.
14547 foreach(list_item
, querytree_list
)
14549 Node
*stm
= (Node
*) lfirst(list_item
);
14550 AlteredTableInfo
*tab
;
14552 tab
= ATGetQueueEntry(wqueue
, rel
);
14554 if (IsA(stm
, IndexStmt
))
14556 IndexStmt
*stmt
= (IndexStmt
*) stm
;
14557 AlterTableCmd
*newcmd
;
14560 TryReuseIndex(oldId
, stmt
);
14561 stmt
->reset_default_tblspc
= true;
14562 /* keep the index's comment */
14563 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
14565 newcmd
= makeNode(AlterTableCmd
);
14566 newcmd
->subtype
= AT_ReAddIndex
;
14567 newcmd
->def
= (Node
*) stmt
;
14568 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14569 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
14571 else if (IsA(stm
, AlterTableStmt
))
14573 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
14576 foreach(lcmd
, stmt
->cmds
)
14578 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
14580 if (cmd
->subtype
== AT_AddIndex
)
14582 IndexStmt
*indstmt
;
14585 indstmt
= castNode(IndexStmt
, cmd
->def
);
14586 indoid
= get_constraint_index(oldId
);
14589 TryReuseIndex(indoid
, indstmt
);
14590 /* keep any comment on the index */
14591 indstmt
->idxcomment
= GetComment(indoid
,
14592 RelationRelationId
, 0);
14593 indstmt
->reset_default_tblspc
= true;
14595 cmd
->subtype
= AT_ReAddIndex
;
14596 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
14597 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
14599 /* recreate any comment on the constraint */
14600 RebuildConstraintComment(tab
,
14607 else if (cmd
->subtype
== AT_AddConstraint
)
14609 Constraint
*con
= castNode(Constraint
, cmd
->def
);
14611 con
->old_pktable_oid
= refRelId
;
14612 /* rewriting neither side of a FK */
14613 if (con
->contype
== CONSTR_FOREIGN
&&
14614 !rewrite
&& tab
->rewrite
== 0)
14615 TryReuseForeignKey(oldId
, con
);
14616 con
->reset_default_tblspc
= true;
14617 cmd
->subtype
= AT_ReAddConstraint
;
14618 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14619 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14621 /* recreate any comment on the constraint */
14622 RebuildConstraintComment(tab
,
14623 AT_PASS_OLD_CONSTR
,
14629 else if (cmd
->subtype
== AT_SetAttNotNull
)
14632 * The parser will create AT_AttSetNotNull subcommands for
14633 * columns of PRIMARY KEY indexes/constraints, but we need
14634 * not do anything with them here, because the columns'
14635 * NOT NULL marks will already have been propagated into
14636 * the new table definition.
14640 elog(ERROR
, "unexpected statement subtype: %d",
14641 (int) cmd
->subtype
);
14644 else if (IsA(stm
, AlterDomainStmt
))
14646 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
14648 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
14650 Constraint
*con
= castNode(Constraint
, stmt
->def
);
14651 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14653 cmd
->subtype
= AT_ReAddDomainConstraint
;
14654 cmd
->def
= (Node
*) stmt
;
14655 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14656 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14658 /* recreate any comment on the constraint */
14659 RebuildConstraintComment(tab
,
14660 AT_PASS_OLD_CONSTR
,
14667 elog(ERROR
, "unexpected statement subtype: %d",
14668 (int) stmt
->subtype
);
14670 else if (IsA(stm
, CreateStatsStmt
))
14672 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
14673 AlterTableCmd
*newcmd
;
14675 /* keep the statistics object's comment */
14676 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
14678 newcmd
= makeNode(AlterTableCmd
);
14679 newcmd
->subtype
= AT_ReAddStatistics
;
14680 newcmd
->def
= (Node
*) stmt
;
14681 tab
->subcmds
[AT_PASS_MISC
] =
14682 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
14685 elog(ERROR
, "unexpected statement type: %d",
14686 (int) nodeTag(stm
));
14689 relation_close(rel
, NoLock
);
14693 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14694 * for a table or domain constraint that is being rebuilt.
14696 * objid is the OID of the constraint.
14697 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14698 * as a string list) for a domain constraint.
14699 * (We could dig that info, as well as the conname, out of the pg_constraint
14700 * entry; but callers already have them so might as well pass them.)
14703 RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
, Oid objid
,
14704 Relation rel
, List
*domname
,
14705 const char *conname
)
14709 AlterTableCmd
*newcmd
;
14711 /* Look for comment for object wanted, and leave if none */
14712 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
14713 if (comment_str
== NULL
)
14716 /* Build CommentStmt node, copying all input data for safety */
14717 cmd
= makeNode(CommentStmt
);
14720 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
14721 cmd
->object
= (Node
*)
14722 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
14723 makeString(pstrdup(RelationGetRelationName(rel
))),
14724 makeString(pstrdup(conname
)));
14728 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
14729 cmd
->object
= (Node
*)
14730 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
14731 makeString(pstrdup(conname
)));
14733 cmd
->comment
= comment_str
;
14735 /* Append it to list of commands */
14736 newcmd
= makeNode(AlterTableCmd
);
14737 newcmd
->subtype
= AT_ReAddComment
;
14738 newcmd
->def
= (Node
*) cmd
;
14739 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
14743 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14744 * for the real analysis, then mutates the IndexStmt based on that verdict.
14747 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
14749 if (CheckIndexCompatible(oldId
,
14750 stmt
->accessMethod
,
14752 stmt
->excludeOpNames
,
14753 stmt
->iswithoutoverlaps
))
14755 Relation irel
= index_open(oldId
, NoLock
);
14757 /* If it's a partitioned index, there is no storage to share. */
14758 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
14760 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
14761 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
14762 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
14764 index_close(irel
, NoLock
);
14769 * Subroutine for ATPostAlterTypeParse().
14771 * Stash the old P-F equality operator into the Constraint node, for possible
14772 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14773 * this constraint can be skipped.
14776 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
14785 Assert(con
->contype
== CONSTR_FOREIGN
);
14786 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
14788 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14789 if (!HeapTupleIsValid(tup
)) /* should not happen */
14790 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14792 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
14793 Anum_pg_constraint_conpfeqop
);
14794 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
14795 numkeys
= ARR_DIMS(arr
)[0];
14796 /* test follows the one in ri_FetchConstraintInfo() */
14797 if (ARR_NDIM(arr
) != 1 ||
14798 ARR_HASNULL(arr
) ||
14799 ARR_ELEMTYPE(arr
) != OIDOID
)
14800 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
14801 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
14803 /* stash a List of the operator Oids in our Constraint node */
14804 for (i
= 0; i
< numkeys
; i
++)
14805 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
14807 ReleaseSysCache(tup
);
14811 * ALTER COLUMN .. OPTIONS ( ... )
14813 * Returns the address of the modified column
14815 static ObjectAddress
14816 ATExecAlterColumnGenericOptions(Relation rel
,
14817 const char *colName
,
14823 ForeignServer
*server
;
14824 ForeignDataWrapper
*fdw
;
14826 HeapTuple newtuple
;
14828 Datum repl_val
[Natts_pg_attribute
];
14829 bool repl_null
[Natts_pg_attribute
];
14830 bool repl_repl
[Natts_pg_attribute
];
14832 Form_pg_foreign_table fttableform
;
14833 Form_pg_attribute atttableform
;
14835 ObjectAddress address
;
14837 if (options
== NIL
)
14838 return InvalidObjectAddress
;
14840 /* First, determine FDW validator associated to the foreign table. */
14841 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
14842 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
14843 if (!HeapTupleIsValid(tuple
))
14845 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14846 errmsg("foreign table \"%s\" does not exist",
14847 RelationGetRelationName(rel
))));
14848 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
14849 server
= GetForeignServer(fttableform
->ftserver
);
14850 fdw
= GetForeignDataWrapper(server
->fdwid
);
14852 table_close(ftrel
, AccessShareLock
);
14853 ReleaseSysCache(tuple
);
14855 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
14856 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
14857 if (!HeapTupleIsValid(tuple
))
14859 (errcode(ERRCODE_UNDEFINED_COLUMN
),
14860 errmsg("column \"%s\" of relation \"%s\" does not exist",
14861 colName
, RelationGetRelationName(rel
))));
14863 /* Prevent them from altering a system attribute */
14864 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
14865 attnum
= atttableform
->attnum
;
14868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14869 errmsg("cannot alter system column \"%s\"", colName
)));
14872 /* Initialize buffers for new tuple values */
14873 memset(repl_val
, 0, sizeof(repl_val
));
14874 memset(repl_null
, false, sizeof(repl_null
));
14875 memset(repl_repl
, false, sizeof(repl_repl
));
14877 /* Extract the current options */
14878 datum
= SysCacheGetAttr(ATTNAME
,
14880 Anum_pg_attribute_attfdwoptions
,
14883 datum
= PointerGetDatum(NULL
);
14885 /* Transform the options */
14886 datum
= transformGenericOptions(AttributeRelationId
,
14889 fdw
->fdwvalidator
);
14891 if (PointerIsValid(DatumGetPointer(datum
)))
14892 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
14894 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14896 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14898 /* Everything looks good - update the tuple */
14900 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
14901 repl_val
, repl_null
, repl_repl
);
14903 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
14905 InvokeObjectPostAlterHook(RelationRelationId
,
14906 RelationGetRelid(rel
),
14907 atttableform
->attnum
);
14908 ObjectAddressSubSet(address
, RelationRelationId
,
14909 RelationGetRelid(rel
), attnum
);
14911 ReleaseSysCache(tuple
);
14913 table_close(attrel
, RowExclusiveLock
);
14915 heap_freetuple(newtuple
);
14921 * ALTER TABLE OWNER
14923 * recursing is true if we are recursing from a table to its indexes,
14924 * sequences, or toast table. We don't allow the ownership of those things to
14925 * be changed separately from the parent table. Also, we can skip permission
14926 * checks (this is necessary not just an optimization, else we'd fail to
14927 * handle toast tables properly).
14929 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14930 * free-standing composite type.
14933 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
14935 Relation target_rel
;
14936 Relation class_rel
;
14938 Form_pg_class tuple_class
;
14941 * Get exclusive lock till end of transaction on the target table. Use
14942 * relation_open so that we can work on indexes and sequences.
14944 target_rel
= relation_open(relationOid
, lockmode
);
14946 /* Get its pg_class tuple, too */
14947 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
14949 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
14950 if (!HeapTupleIsValid(tuple
))
14951 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
14952 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
14954 /* Can we change the ownership of this tuple? */
14955 switch (tuple_class
->relkind
)
14957 case RELKIND_RELATION
:
14959 case RELKIND_MATVIEW
:
14960 case RELKIND_FOREIGN_TABLE
:
14961 case RELKIND_PARTITIONED_TABLE
:
14962 /* ok to change owner */
14964 case RELKIND_INDEX
:
14968 * Because ALTER INDEX OWNER used to be allowed, and in fact
14969 * is generated by old versions of pg_dump, we give a warning
14970 * and do nothing rather than erroring out. Also, to avoid
14971 * unnecessary chatter while restoring those old dumps, say
14972 * nothing at all if the command would be a no-op anyway.
14974 if (tuple_class
->relowner
!= newOwnerId
)
14976 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14977 errmsg("cannot change owner of index \"%s\"",
14978 NameStr(tuple_class
->relname
)),
14979 errhint("Change the ownership of the index's table instead.")));
14980 /* quick hack to exit via the no-op path */
14981 newOwnerId
= tuple_class
->relowner
;
14984 case RELKIND_PARTITIONED_INDEX
:
14988 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14989 errmsg("cannot change owner of index \"%s\"",
14990 NameStr(tuple_class
->relname
)),
14991 errhint("Change the ownership of the index's table instead.")));
14993 case RELKIND_SEQUENCE
:
14995 tuple_class
->relowner
!= newOwnerId
)
14997 /* if it's an owned sequence, disallow changing it by itself */
15001 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
15002 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
15004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15005 errmsg("cannot change owner of sequence \"%s\"",
15006 NameStr(tuple_class
->relname
)),
15007 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15008 NameStr(tuple_class
->relname
),
15009 get_rel_name(tableId
))));
15012 case RELKIND_COMPOSITE_TYPE
:
15016 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15017 errmsg("\"%s\" is a composite type",
15018 NameStr(tuple_class
->relname
)),
15019 /* translator: %s is an SQL ALTER command */
15020 errhint("Use %s instead.",
15023 case RELKIND_TOASTVALUE
:
15029 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15030 errmsg("cannot change owner of relation \"%s\"",
15031 NameStr(tuple_class
->relname
)),
15032 errdetail_relkind_not_supported(tuple_class
->relkind
)));
15036 * If the new owner is the same as the existing owner, consider the
15037 * command to have succeeded. This is for dump restoration purposes.
15039 if (tuple_class
->relowner
!= newOwnerId
)
15041 Datum repl_val
[Natts_pg_class
];
15042 bool repl_null
[Natts_pg_class
];
15043 bool repl_repl
[Natts_pg_class
];
15047 HeapTuple newtuple
;
15049 /* skip permission checks when recursing to index or toast table */
15052 /* Superusers can always do it */
15055 Oid namespaceOid
= tuple_class
->relnamespace
;
15056 AclResult aclresult
;
15058 /* Otherwise, must be owner of the existing object */
15059 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
15060 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
15061 RelationGetRelationName(target_rel
));
15063 /* Must be able to become new owner */
15064 check_can_set_role(GetUserId(), newOwnerId
);
15066 /* New owner must have CREATE privilege on namespace */
15067 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
15069 if (aclresult
!= ACLCHECK_OK
)
15070 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
15071 get_namespace_name(namespaceOid
));
15075 memset(repl_null
, false, sizeof(repl_null
));
15076 memset(repl_repl
, false, sizeof(repl_repl
));
15078 repl_repl
[Anum_pg_class_relowner
- 1] = true;
15079 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
15082 * Determine the modified ACL for the new owner. This is only
15083 * necessary when the ACL is non-null.
15085 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
15086 Anum_pg_class_relacl
,
15090 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15091 tuple_class
->relowner
, newOwnerId
);
15092 repl_repl
[Anum_pg_class_relacl
- 1] = true;
15093 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
15096 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
15098 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
15100 heap_freetuple(newtuple
);
15103 * We must similarly update any per-column ACLs to reflect the new
15104 * owner; for neatness reasons that's split out as a subroutine.
15106 change_owner_fix_column_acls(relationOid
,
15107 tuple_class
->relowner
,
15111 * Update owner dependency reference, if any. A composite type has
15112 * none, because it's tracked for the pg_type entry instead of here;
15113 * indexes and TOAST tables don't have their own entries either.
15115 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
15116 tuple_class
->relkind
!= RELKIND_INDEX
&&
15117 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
15118 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
15119 changeDependencyOnOwner(RelationRelationId
, relationOid
,
15123 * Also change the ownership of the table's row type, if it has one
15125 if (OidIsValid(tuple_class
->reltype
))
15126 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
15129 * If we are operating on a table or materialized view, also change
15130 * the ownership of any indexes and sequences that belong to the
15131 * relation, as well as its toast table (if it has one).
15133 if (tuple_class
->relkind
== RELKIND_RELATION
||
15134 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
15135 tuple_class
->relkind
== RELKIND_MATVIEW
||
15136 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
15138 List
*index_oid_list
;
15141 /* Find all the indexes belonging to this relation */
15142 index_oid_list
= RelationGetIndexList(target_rel
);
15144 /* For each index, recursively change its ownership */
15145 foreach(i
, index_oid_list
)
15146 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
15148 list_free(index_oid_list
);
15151 /* If it has a toast table, recurse to change its ownership */
15152 if (tuple_class
->reltoastrelid
!= InvalidOid
)
15153 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
15156 /* If it has dependent sequences, recurse to change them too */
15157 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
15160 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
15162 ReleaseSysCache(tuple
);
15163 table_close(class_rel
, RowExclusiveLock
);
15164 relation_close(target_rel
, NoLock
);
15168 * change_owner_fix_column_acls
15170 * Helper function for ATExecChangeOwner. Scan the columns of the table
15171 * and fix any non-null column ACLs to reflect the new owner.
15174 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
15176 Relation attRelation
;
15178 ScanKeyData key
[1];
15179 HeapTuple attributeTuple
;
15181 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
15182 ScanKeyInit(&key
[0],
15183 Anum_pg_attribute_attrelid
,
15184 BTEqualStrategyNumber
, F_OIDEQ
,
15185 ObjectIdGetDatum(relationOid
));
15186 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
15187 true, NULL
, 1, key
);
15188 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
15190 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
15191 Datum repl_val
[Natts_pg_attribute
];
15192 bool repl_null
[Natts_pg_attribute
];
15193 bool repl_repl
[Natts_pg_attribute
];
15197 HeapTuple newtuple
;
15199 /* Ignore dropped columns */
15200 if (att
->attisdropped
)
15203 aclDatum
= heap_getattr(attributeTuple
,
15204 Anum_pg_attribute_attacl
,
15205 RelationGetDescr(attRelation
),
15207 /* Null ACLs do not require changes */
15211 memset(repl_null
, false, sizeof(repl_null
));
15212 memset(repl_repl
, false, sizeof(repl_repl
));
15214 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
15215 oldOwnerId
, newOwnerId
);
15216 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
15217 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
15219 newtuple
= heap_modify_tuple(attributeTuple
,
15220 RelationGetDescr(attRelation
),
15221 repl_val
, repl_null
, repl_repl
);
15223 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
15225 heap_freetuple(newtuple
);
15227 systable_endscan(scan
);
15228 table_close(attRelation
, RowExclusiveLock
);
15232 * change_owner_recurse_to_sequences
15234 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15235 * for sequences that are dependent on serial columns, and changes their
15239 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
15243 ScanKeyData key
[2];
15247 * SERIAL sequences are those having an auto dependency on one of the
15248 * table's columns (we don't care *which* column, exactly).
15250 depRel
= table_open(DependRelationId
, AccessShareLock
);
15252 ScanKeyInit(&key
[0],
15253 Anum_pg_depend_refclassid
,
15254 BTEqualStrategyNumber
, F_OIDEQ
,
15255 ObjectIdGetDatum(RelationRelationId
));
15256 ScanKeyInit(&key
[1],
15257 Anum_pg_depend_refobjid
,
15258 BTEqualStrategyNumber
, F_OIDEQ
,
15259 ObjectIdGetDatum(relationOid
));
15260 /* we leave refobjsubid unspecified */
15262 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
15265 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
15267 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
15270 /* skip dependencies other than auto dependencies on columns */
15271 if (depForm
->refobjsubid
== 0 ||
15272 depForm
->classid
!= RelationRelationId
||
15273 depForm
->objsubid
!= 0 ||
15274 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
15277 /* Use relation_open just in case it's an index */
15278 seqRel
= relation_open(depForm
->objid
, lockmode
);
15280 /* skip non-sequence relations */
15281 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
15283 /* No need to keep the lock */
15284 relation_close(seqRel
, lockmode
);
15288 /* We don't need to close the sequence while we alter it. */
15289 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
15291 /* Now we can close it. Keep the lock till end of transaction. */
15292 relation_close(seqRel
, NoLock
);
15295 systable_endscan(scan
);
15297 relation_close(depRel
, AccessShareLock
);
15301 * ALTER TABLE CLUSTER ON
15303 * The only thing we have to do is to change the indisclustered bits.
15305 * Return the address of the new clustering index.
15307 static ObjectAddress
15308 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
15311 ObjectAddress address
;
15313 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
15315 if (!OidIsValid(indexOid
))
15317 (errcode(ERRCODE_UNDEFINED_OBJECT
),
15318 errmsg("index \"%s\" for table \"%s\" does not exist",
15319 indexName
, RelationGetRelationName(rel
))));
15321 /* Check index is valid to cluster on */
15322 check_index_is_clusterable(rel
, indexOid
, lockmode
);
15324 /* And do the work */
15325 mark_index_clustered(rel
, indexOid
, false);
15327 ObjectAddressSet(address
,
15328 RelationRelationId
, indexOid
);
15334 * ALTER TABLE SET WITHOUT CLUSTER
15336 * We have to find any indexes on the table that have indisclustered bit
15337 * set and turn it off.
15340 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
15342 mark_index_clustered(rel
, InvalidOid
, false);
15346 * Preparation phase for SET ACCESS METHOD
15348 * Check that the access method exists and determine whether a change is
15352 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
15357 * Look up the access method name and check that it differs from the
15358 * table's current AM. If DEFAULT was specified for a partitioned table
15359 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15361 if (amname
!= NULL
)
15362 amoid
= get_table_am_oid(amname
, false);
15363 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15364 amoid
= InvalidOid
;
15366 amoid
= get_table_am_oid(default_table_access_method
, false);
15368 /* if it's a match, phase 3 doesn't need to do anything */
15369 if (rel
->rd_rel
->relam
== amoid
)
15372 /* Save info for Phase 3 to do the real work */
15373 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
15374 tab
->newAccessMethod
= amoid
;
15375 tab
->chgAccessMethod
= true;
15379 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15380 * storage that have an interest in preserving AM.
15382 * Since these have no storage, setting the access method is a catalog only
15386 ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
)
15389 Oid oldAccessMethodId
;
15391 Form_pg_class rd_rel
;
15392 Oid reloid
= RelationGetRelid(rel
);
15395 * Shouldn't be called on relations having storage; these are processed in
15398 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15400 /* Get a modifiable copy of the relation's pg_class row. */
15401 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
15403 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
15404 if (!HeapTupleIsValid(tuple
))
15405 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
15406 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
15408 /* Update the pg_class row. */
15409 oldAccessMethodId
= rd_rel
->relam
;
15410 rd_rel
->relam
= newAccessMethodId
;
15412 /* Leave if no update required */
15413 if (rd_rel
->relam
== oldAccessMethodId
)
15415 heap_freetuple(tuple
);
15416 table_close(pg_class
, RowExclusiveLock
);
15420 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
15423 * Update the dependency on the new access method. No dependency is added
15424 * if the new access method is InvalidOid (default case). Be very careful
15425 * that this has to compare the previous value stored in pg_class with the
15428 if (!OidIsValid(oldAccessMethodId
) && OidIsValid(rd_rel
->relam
))
15430 ObjectAddress relobj
,
15434 * New access method is defined and there was no dependency
15435 * previously, so record a new one.
15437 ObjectAddressSet(relobj
, RelationRelationId
, reloid
);
15438 ObjectAddressSet(referenced
, AccessMethodRelationId
, rd_rel
->relam
);
15439 recordDependencyOn(&relobj
, &referenced
, DEPENDENCY_NORMAL
);
15441 else if (OidIsValid(oldAccessMethodId
) &&
15442 !OidIsValid(rd_rel
->relam
))
15445 * There was an access method defined, and no new one, so just remove
15446 * the existing dependency.
15448 deleteDependencyRecordsForClass(RelationRelationId
, reloid
,
15449 AccessMethodRelationId
,
15450 DEPENDENCY_NORMAL
);
15454 Assert(OidIsValid(oldAccessMethodId
) &&
15455 OidIsValid(rd_rel
->relam
));
15457 /* Both are valid, so update the dependency */
15458 changeDependencyFor(RelationRelationId
, reloid
,
15459 AccessMethodRelationId
,
15460 oldAccessMethodId
, rd_rel
->relam
);
15463 /* make the relam and dependency changes visible */
15464 CommandCounterIncrement();
15466 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15468 heap_freetuple(tuple
);
15469 table_close(pg_class
, RowExclusiveLock
);
15473 * ALTER TABLE SET TABLESPACE
15476 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
15480 /* Check that the tablespace exists */
15481 tablespaceId
= get_tablespace_oid(tablespacename
, false);
15483 /* Check permissions except when moving to database's default */
15484 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
15486 AclResult aclresult
;
15488 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
15489 if (aclresult
!= ACLCHECK_OK
)
15490 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
15493 /* Save info for Phase 3 to do the real work */
15494 if (OidIsValid(tab
->newTableSpace
))
15496 (errcode(ERRCODE_SYNTAX_ERROR
),
15497 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15499 tab
->newTableSpace
= tablespaceId
;
15503 * Set, reset, or replace reloptions.
15506 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
15512 HeapTuple newtuple
;
15516 Datum repl_val
[Natts_pg_class
];
15517 bool repl_null
[Natts_pg_class
];
15518 bool repl_repl
[Natts_pg_class
];
15519 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
15521 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
15522 return; /* nothing to do */
15524 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
15526 /* Fetch heap tuple */
15527 relid
= RelationGetRelid(rel
);
15528 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
15529 if (!HeapTupleIsValid(tuple
))
15530 elog(ERROR
, "cache lookup failed for relation %u", relid
);
15532 if (operation
== AT_ReplaceRelOptions
)
15535 * If we're supposed to replace the reloptions list, we just pretend
15536 * there were none before.
15543 /* Get the old reloptions */
15544 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15548 /* Generate new proposed reloptions (text array) */
15549 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15550 defList
, NULL
, validnsps
, false,
15551 operation
== AT_ResetRelOptions
);
15554 switch (rel
->rd_rel
->relkind
)
15556 case RELKIND_RELATION
:
15557 case RELKIND_TOASTVALUE
:
15558 case RELKIND_MATVIEW
:
15559 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
15561 case RELKIND_PARTITIONED_TABLE
:
15562 (void) partitioned_table_reloptions(newOptions
, true);
15565 (void) view_reloptions(newOptions
, true);
15567 case RELKIND_INDEX
:
15568 case RELKIND_PARTITIONED_INDEX
:
15569 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
15573 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15574 errmsg("cannot set options for relation \"%s\"",
15575 RelationGetRelationName(rel
)),
15576 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
15580 /* Special-case validation of view options */
15581 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
15583 Query
*view_query
= get_view_query(rel
);
15584 List
*view_options
= untransformRelOptions(newOptions
);
15586 bool check_option
= false;
15588 foreach(cell
, view_options
)
15590 DefElem
*defel
= (DefElem
*) lfirst(cell
);
15592 if (strcmp(defel
->defname
, "check_option") == 0)
15593 check_option
= true;
15597 * If the check option is specified, look to see if the view is
15598 * actually auto-updatable or not.
15602 const char *view_updatable_error
=
15603 view_query_is_auto_updatable(view_query
, true);
15605 if (view_updatable_error
)
15607 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15608 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15609 errhint("%s", _(view_updatable_error
))));
15614 * All we need do here is update the pg_class row; the new options will be
15615 * propagated into relcaches during post-commit cache inval.
15617 memset(repl_val
, 0, sizeof(repl_val
));
15618 memset(repl_null
, false, sizeof(repl_null
));
15619 memset(repl_repl
, false, sizeof(repl_repl
));
15621 if (newOptions
!= (Datum
) 0)
15622 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15624 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15626 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15628 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15629 repl_val
, repl_null
, repl_repl
);
15631 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15633 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15635 heap_freetuple(newtuple
);
15637 ReleaseSysCache(tuple
);
15639 /* repeat the whole exercise for the toast table, if there's one */
15640 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
15643 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
15645 toastrel
= table_open(toastid
, lockmode
);
15647 /* Fetch heap tuple */
15648 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
15649 if (!HeapTupleIsValid(tuple
))
15650 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
15652 if (operation
== AT_ReplaceRelOptions
)
15655 * If we're supposed to replace the reloptions list, we just
15656 * pretend there were none before.
15663 /* Get the old reloptions */
15664 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15668 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15669 defList
, "toast", validnsps
, false,
15670 operation
== AT_ResetRelOptions
);
15672 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
15674 memset(repl_val
, 0, sizeof(repl_val
));
15675 memset(repl_null
, false, sizeof(repl_null
));
15676 memset(repl_repl
, false, sizeof(repl_repl
));
15678 if (newOptions
!= (Datum
) 0)
15679 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15681 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15683 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15685 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15686 repl_val
, repl_null
, repl_repl
);
15688 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15690 InvokeObjectPostAlterHookArg(RelationRelationId
,
15691 RelationGetRelid(toastrel
), 0,
15694 heap_freetuple(newtuple
);
15696 ReleaseSysCache(tuple
);
15698 table_close(toastrel
, NoLock
);
15701 table_close(pgclass
, RowExclusiveLock
);
15705 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15706 * rewriting to be done, so we just want to copy the data as fast as possible.
15709 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
15713 RelFileNumber newrelfilenumber
;
15714 RelFileLocator newrlocator
;
15715 List
*reltoastidxids
= NIL
;
15719 * Need lock here in case we are recursing to toast table or index
15721 rel
= relation_open(tableOid
, lockmode
);
15723 /* Check first if relation can be moved to new tablespace */
15724 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15726 InvokeObjectPostAlterHook(RelationRelationId
,
15727 RelationGetRelid(rel
), 0);
15728 relation_close(rel
, NoLock
);
15732 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
15733 /* Fetch the list of indexes on toast relation if necessary */
15734 if (OidIsValid(reltoastrelid
))
15736 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
15738 reltoastidxids
= RelationGetIndexList(toastRel
);
15739 relation_close(toastRel
, lockmode
);
15743 * Relfilenumbers are not unique in databases across tablespaces, so we
15744 * need to allocate a new one in the new tablespace.
15746 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
15747 rel
->rd_rel
->relpersistence
);
15749 /* Open old and new relation */
15750 newrlocator
= rel
->rd_locator
;
15751 newrlocator
.relNumber
= newrelfilenumber
;
15752 newrlocator
.spcOid
= newTableSpace
;
15754 /* hand off to AM to actually create new rel storage and copy the data */
15755 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
15757 index_copy_data(rel
, newrlocator
);
15761 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
15762 table_relation_copy_data(rel
, &newrlocator
);
15766 * Update the pg_class row.
15768 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15769 * executed on pg_class or its indexes (the above copy wouldn't contain
15770 * the updated pg_class entry), but that's forbidden with
15771 * CheckRelationTableSpaceMove().
15773 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
15775 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15777 RelationAssumeNewRelfilelocator(rel
);
15779 relation_close(rel
, NoLock
);
15781 /* Make sure the reltablespace change is visible */
15782 CommandCounterIncrement();
15784 /* Move associated toast relation and/or indexes, too */
15785 if (OidIsValid(reltoastrelid
))
15786 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
15787 foreach(lc
, reltoastidxids
)
15788 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
15791 list_free(reltoastidxids
);
15795 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15796 * storage that have an interest in preserving tablespace.
15798 * Since these have no storage the tablespace can be updated with a simple
15799 * metadata only operation to update the tablespace.
15802 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
15805 * Shouldn't be called on relations having storage; these are processed in
15808 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15810 /* check if relation can be moved to its new tablespace */
15811 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15813 InvokeObjectPostAlterHook(RelationRelationId
,
15814 RelationGetRelid(rel
),
15819 /* Update can be done, so change reltablespace */
15820 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
15822 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15824 /* Make sure the reltablespace change is visible */
15825 CommandCounterIncrement();
15829 * Alter Table ALL ... SET TABLESPACE
15831 * Allows a user to move all objects of some type in a given tablespace in the
15832 * current database to another tablespace. Objects can be chosen based on the
15833 * owner of the object also, to allow users to move only their objects.
15834 * The user must have CREATE rights on the new tablespace, as usual. The main
15835 * permissions handling is done by the lower-level table move function.
15837 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15838 * lock can't be acquired then we ereport(ERROR).
15841 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
15843 List
*relations
= NIL
;
15845 ScanKeyData key
[1];
15847 TableScanDesc scan
;
15849 Oid orig_tablespaceoid
;
15850 Oid new_tablespaceoid
;
15851 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
15853 /* Ensure we were not asked to move something we can't */
15854 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
15855 stmt
->objtype
!= OBJECT_MATVIEW
)
15857 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15858 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15860 /* Get the orig and new tablespace OIDs */
15861 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
15862 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
15864 /* Can't move shared relations in to or out of pg_global */
15865 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15866 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
15867 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
15869 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15870 errmsg("cannot move relations in to or out of pg_global tablespace")));
15873 * Must have CREATE rights on the new tablespace, unless it is the
15874 * database default tablespace (which all users implicitly have CREATE
15877 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
15879 AclResult aclresult
;
15881 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
15883 if (aclresult
!= ACLCHECK_OK
)
15884 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
15885 get_tablespace_name(new_tablespaceoid
));
15889 * Now that the checks are done, check if we should set either to
15890 * InvalidOid because it is our database's default tablespace.
15892 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
15893 orig_tablespaceoid
= InvalidOid
;
15895 if (new_tablespaceoid
== MyDatabaseTableSpace
)
15896 new_tablespaceoid
= InvalidOid
;
15899 if (orig_tablespaceoid
== new_tablespaceoid
)
15900 return new_tablespaceoid
;
15903 * Walk the list of objects in the tablespace and move them. This will
15904 * only find objects in our database, of course.
15906 ScanKeyInit(&key
[0],
15907 Anum_pg_class_reltablespace
,
15908 BTEqualStrategyNumber
, F_OIDEQ
,
15909 ObjectIdGetDatum(orig_tablespaceoid
));
15911 rel
= table_open(RelationRelationId
, AccessShareLock
);
15912 scan
= table_beginscan_catalog(rel
, 1, key
);
15913 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
15915 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
15916 Oid relOid
= relForm
->oid
;
15919 * Do not move objects in pg_catalog as part of this, if an admin
15920 * really wishes to do so, they can issue the individual ALTER
15921 * commands directly.
15923 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15924 * (TOAST will be moved with the main table).
15926 if (IsCatalogNamespace(relForm
->relnamespace
) ||
15927 relForm
->relisshared
||
15928 isAnyTempNamespace(relForm
->relnamespace
) ||
15929 IsToastNamespace(relForm
->relnamespace
))
15932 /* Only move the object type requested */
15933 if ((stmt
->objtype
== OBJECT_TABLE
&&
15934 relForm
->relkind
!= RELKIND_RELATION
&&
15935 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
15936 (stmt
->objtype
== OBJECT_INDEX
&&
15937 relForm
->relkind
!= RELKIND_INDEX
&&
15938 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
15939 (stmt
->objtype
== OBJECT_MATVIEW
&&
15940 relForm
->relkind
!= RELKIND_MATVIEW
))
15943 /* Check if we are only moving objects owned by certain roles */
15944 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
15948 * Handle permissions-checking here since we are locking the tables
15949 * and also to avoid doing a bunch of work only to fail part-way. Note
15950 * that permissions will also be checked by AlterTableInternal().
15952 * Caller must be considered an owner on the table to move it.
15954 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
15955 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
15956 NameStr(relForm
->relname
));
15958 if (stmt
->nowait
&&
15959 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
15961 (errcode(ERRCODE_OBJECT_IN_USE
),
15962 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15963 get_namespace_name(relForm
->relnamespace
),
15964 NameStr(relForm
->relname
))));
15966 LockRelationOid(relOid
, AccessExclusiveLock
);
15968 /* Add to our list of objects to move */
15969 relations
= lappend_oid(relations
, relOid
);
15972 table_endscan(scan
);
15973 table_close(rel
, AccessShareLock
);
15975 if (relations
== NIL
)
15977 (errcode(ERRCODE_NO_DATA_FOUND
),
15978 errmsg("no matching relations in tablespace \"%s\" found",
15979 orig_tablespaceoid
== InvalidOid
? "(database default)" :
15980 get_tablespace_name(orig_tablespaceoid
))));
15982 /* Everything is locked, loop through and move all of the relations. */
15983 foreach(l
, relations
)
15986 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
15988 cmd
->subtype
= AT_SetTableSpace
;
15989 cmd
->name
= stmt
->new_tablespacename
;
15991 cmds
= lappend(cmds
, cmd
);
15993 EventTriggerAlterTableStart((Node
*) stmt
);
15994 /* OID is set by AlterTableInternal */
15995 AlterTableInternal(lfirst_oid(l
), cmds
, false);
15996 EventTriggerAlterTableEnd();
15999 return new_tablespaceoid
;
16003 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
16005 SMgrRelation dstrel
;
16008 * Since we copy the file directly without looking at the shared buffers,
16009 * we'd better first flush out any pages of the source relation that are
16010 * in shared buffers. We assume no new changes will be made while we are
16011 * holding exclusive lock on the rel.
16013 FlushRelationBuffers(rel
);
16016 * Create and copy all forks of the relation, and schedule unlinking of
16017 * old physical files.
16019 * NOTE: any conflict in relfilenumber value will be caught in
16020 * RelationCreateStorage().
16022 dstrel
= RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
16024 /* copy main fork */
16025 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
16026 rel
->rd_rel
->relpersistence
);
16028 /* copy those extra forks that exist */
16029 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
16030 forkNum
<= MAX_FORKNUM
; forkNum
++)
16032 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
16034 smgrcreate(dstrel
, forkNum
, false);
16037 * WAL log creation if the relation is persistent, or this is the
16038 * init fork of an unlogged relation.
16040 if (RelationIsPermanent(rel
) ||
16041 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
16042 forkNum
== INIT_FORKNUM
))
16043 log_smgrcreate(&newrlocator
, forkNum
);
16044 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
16045 rel
->rd_rel
->relpersistence
);
16049 /* drop old relation, and close new one */
16050 RelationDropStorage(rel
);
16055 * ALTER TABLE ENABLE/DISABLE TRIGGER
16057 * We just pass this off to trigger.c.
16060 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
16061 char fires_when
, bool skip_system
, bool recurse
,
16064 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
16065 fires_when
, skip_system
, recurse
,
16068 InvokeObjectPostAlterHook(RelationRelationId
,
16069 RelationGetRelid(rel
), 0);
16073 * ALTER TABLE ENABLE/DISABLE RULE
16075 * We just pass this off to rewriteDefine.c.
16078 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
16079 char fires_when
, LOCKMODE lockmode
)
16081 EnableDisableRule(rel
, rulename
, fires_when
);
16083 InvokeObjectPostAlterHook(RelationRelationId
,
16084 RelationGetRelid(rel
), 0);
16088 * ALTER TABLE INHERIT
16090 * Add a parent to the child's parents. This verifies that all the columns and
16091 * check constraints of the parent appear in the child and that they have the
16092 * same data types and expressions.
16095 ATPrepAddInherit(Relation child_rel
)
16097 if (child_rel
->rd_rel
->reloftype
)
16099 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16100 errmsg("cannot change inheritance of typed table")));
16102 if (child_rel
->rd_rel
->relispartition
)
16104 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16105 errmsg("cannot change inheritance of a partition")));
16107 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16109 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16110 errmsg("cannot change inheritance of partitioned table")));
16114 * Return the address of the new parent relation.
16116 static ObjectAddress
16117 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16119 Relation parent_rel
;
16121 ObjectAddress address
;
16122 const char *trigger_name
;
16125 * A self-exclusive lock is needed here. See the similar case in
16126 * MergeAttributes() for a full explanation.
16128 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
16131 * Must be owner of both parent and child -- child was checked by
16132 * ATSimplePermissions call in ATPrepCmd
16134 ATSimplePermissions(AT_AddInherit
, parent_rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
16136 /* Permanent rels cannot inherit from temporary ones */
16137 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16138 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
16140 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16141 errmsg("cannot inherit from temporary relation \"%s\"",
16142 RelationGetRelationName(parent_rel
))));
16144 /* If parent rel is temp, it must belong to this session */
16145 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16146 !parent_rel
->rd_islocaltemp
)
16148 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16149 errmsg("cannot inherit from temporary relation of another session")));
16151 /* Ditto for the child */
16152 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
16153 !child_rel
->rd_islocaltemp
)
16155 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16156 errmsg("cannot inherit to temporary relation of another session")));
16158 /* Prevent partitioned tables from becoming inheritance parents */
16159 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16161 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16162 errmsg("cannot inherit from partitioned table \"%s\"",
16163 parent
->relname
)));
16165 /* Likewise for partitions */
16166 if (parent_rel
->rd_rel
->relispartition
)
16168 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16169 errmsg("cannot inherit from a partition")));
16172 * Prevent circularity by seeing if proposed parent inherits from child.
16173 * (In particular, this disallows making a rel inherit from itself.)
16175 * This is not completely bulletproof because of race conditions: in
16176 * multi-level inheritance trees, someone else could concurrently be
16177 * making another inheritance link that closes the loop but does not join
16178 * either of the rels we have locked. Preventing that seems to require
16179 * exclusive locks on the entire inheritance tree, which is a cure worse
16180 * than the disease. find_all_inheritors() will cope with circularity
16181 * anyway, so don't sweat it too much.
16183 * We use weakest lock we can on child's children, namely AccessShareLock.
16185 children
= find_all_inheritors(RelationGetRelid(child_rel
),
16186 AccessShareLock
, NULL
);
16188 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
16190 (errcode(ERRCODE_DUPLICATE_TABLE
),
16191 errmsg("circular inheritance not allowed"),
16192 errdetail("\"%s\" is already a child of \"%s\".",
16194 RelationGetRelationName(child_rel
))));
16197 * If child_rel has row-level triggers with transition tables, we
16198 * currently don't allow it to become an inheritance child. See also
16199 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16201 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
16202 if (trigger_name
!= NULL
)
16204 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16205 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16206 trigger_name
, RelationGetRelationName(child_rel
)),
16207 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16209 /* OK to create inheritance */
16210 CreateInheritance(child_rel
, parent_rel
, false);
16213 * If parent_rel has a primary key, then child_rel has not-null
16214 * constraints that make these columns as non nullable. Make those
16215 * constraints as inherited.
16217 ATInheritAdjustNotNulls(parent_rel
, child_rel
, 1);
16219 ObjectAddressSet(address
, RelationRelationId
,
16220 RelationGetRelid(parent_rel
));
16222 /* keep our lock on the parent relation until commit */
16223 table_close(parent_rel
, NoLock
);
16229 * CreateInheritance
16230 * Catalog manipulation portion of creating inheritance between a child
16231 * table and a parent table.
16233 * Common to ATExecAddInherit() and ATExecAttachPartition().
16236 CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16238 Relation catalogRelation
;
16241 HeapTuple inheritsTuple
;
16244 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16245 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16248 * Check for duplicates in the list of parents, and determine the highest
16249 * inhseqno already present; we'll use the next one for the new parent.
16250 * Also, if proposed child is a partition, it cannot already be
16253 * Note: we do not reject the case where the child already inherits from
16254 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16257 Anum_pg_inherits_inhrelid
,
16258 BTEqualStrategyNumber
, F_OIDEQ
,
16259 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16260 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
16261 true, NULL
, 1, &key
);
16263 /* inhseqno sequences start at 1 */
16265 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16267 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16269 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
16271 (errcode(ERRCODE_DUPLICATE_TABLE
),
16272 errmsg("relation \"%s\" would be inherited from more than once",
16273 RelationGetRelationName(parent_rel
))));
16275 if (inh
->inhseqno
> inhseqno
)
16276 inhseqno
= inh
->inhseqno
;
16278 systable_endscan(scan
);
16280 /* Match up the columns and bump attinhcount as needed */
16281 MergeAttributesIntoExisting(child_rel
, parent_rel
, ispartition
);
16283 /* Match up the constraints and bump coninhcount as needed */
16284 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
16287 * OK, it looks valid. Make the catalog entries that show inheritance.
16289 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
16290 RelationGetRelid(parent_rel
),
16293 parent_rel
->rd_rel
->relkind
==
16294 RELKIND_PARTITIONED_TABLE
);
16296 /* Now we're done with pg_inherits */
16297 table_close(catalogRelation
, RowExclusiveLock
);
16301 * Obtain the source-text form of the constraint expression for a check
16302 * constraint, given its pg_constraint tuple
16305 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
16307 Form_pg_constraint con
;
16312 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
16313 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
16315 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
16317 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
16318 ObjectIdGetDatum(con
->conrelid
));
16319 return TextDatumGetCString(expr
);
16323 * Determine whether two check constraints are functionally equivalent
16325 * The test we apply is to see whether they reverse-compile to the same
16326 * source string. This insulates us from issues like whether attributes
16327 * have the same physical column numbers in parent and child relations.
16330 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
16332 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
16333 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
16335 if (acon
->condeferrable
!= bcon
->condeferrable
||
16336 acon
->condeferred
!= bcon
->condeferred
||
16337 strcmp(decompile_conbin(a
, tupleDesc
),
16338 decompile_conbin(b
, tupleDesc
)) != 0)
16345 * Check columns in child table match up with columns in parent, and increment
16346 * their attinhcount.
16348 * Called by CreateInheritance
16350 * Currently all parent columns must be found in child. Missing columns are an
16351 * error. One day we might consider creating new columns like CREATE TABLE
16352 * does. However, that is widely unpopular --- in the common use case of
16353 * partitioned tables it's a foot-gun.
16355 * The data type must match exactly. If the parent column is NOT NULL then
16356 * the child must be as well. Defaults are not compared, however.
16359 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
)
16362 TupleDesc parent_desc
;
16364 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
16365 parent_desc
= RelationGetDescr(parent_rel
);
16367 for (AttrNumber parent_attno
= 1; parent_attno
<= parent_desc
->natts
; parent_attno
++)
16369 Form_pg_attribute parent_att
= TupleDescAttr(parent_desc
, parent_attno
- 1);
16370 char *parent_attname
= NameStr(parent_att
->attname
);
16373 /* Ignore dropped columns in the parent. */
16374 if (parent_att
->attisdropped
)
16377 /* Find same column in child (matching on column name). */
16378 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
), parent_attname
);
16379 if (HeapTupleIsValid(tuple
))
16381 Form_pg_attribute child_att
= (Form_pg_attribute
) GETSTRUCT(tuple
);
16383 if (parent_att
->atttypid
!= child_att
->atttypid
||
16384 parent_att
->atttypmod
!= child_att
->atttypmod
)
16386 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16387 errmsg("child table \"%s\" has different type for column \"%s\"",
16388 RelationGetRelationName(child_rel
), parent_attname
)));
16390 if (parent_att
->attcollation
!= child_att
->attcollation
)
16392 (errcode(ERRCODE_COLLATION_MISMATCH
),
16393 errmsg("child table \"%s\" has different collation for column \"%s\"",
16394 RelationGetRelationName(child_rel
), parent_attname
)));
16397 * If the parent has a not-null constraint that's not NO INHERIT,
16398 * make sure the child has one too.
16400 * Other constraints are checked elsewhere.
16402 if (parent_att
->attnotnull
&& !child_att
->attnotnull
)
16406 contup
= findNotNullConstraintAttnum(RelationGetRelid(parent_rel
),
16407 parent_att
->attnum
);
16408 if (HeapTupleIsValid(contup
) &&
16409 !((Form_pg_constraint
) GETSTRUCT(contup
))->connoinherit
)
16411 errcode(ERRCODE_DATATYPE_MISMATCH
),
16412 errmsg("column \"%s\" in child table must be marked NOT NULL",
16417 * Child column must be generated if and only if parent column is.
16419 if (parent_att
->attgenerated
&& !child_att
->attgenerated
)
16421 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16422 errmsg("column \"%s\" in child table must be a generated column", parent_attname
)));
16423 if (child_att
->attgenerated
&& !parent_att
->attgenerated
)
16425 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16426 errmsg("column \"%s\" in child table must not be a generated column", parent_attname
)));
16429 * Regular inheritance children are independent enough not to
16430 * inherit identity columns. But partitions are integral part of
16431 * a partitioned table and inherit identity column.
16434 child_att
->attidentity
= parent_att
->attidentity
;
16437 * OK, bump the child column's inheritance count. (If we fail
16438 * later on, this change will just roll back.)
16440 child_att
->attinhcount
++;
16441 if (child_att
->attinhcount
< 0)
16443 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16444 errmsg("too many inheritance parents"));
16447 * In case of partitions, we must enforce that value of attislocal
16448 * is same in all partitions. (Note: there are only inherited
16449 * attributes in partitions)
16451 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16453 Assert(child_att
->attinhcount
== 1);
16454 child_att
->attislocal
= false;
16457 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
16458 heap_freetuple(tuple
);
16463 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16464 errmsg("child table is missing column \"%s\"", parent_attname
)));
16468 table_close(attrrel
, RowExclusiveLock
);
16472 * Check constraints in child table match up with constraints in parent,
16473 * and increment their coninhcount.
16475 * Constraints that are marked ONLY in the parent are ignored.
16477 * Called by CreateInheritance
16479 * Currently all constraints in parent must be present in the child. One day we
16480 * may consider adding new constraints like CREATE TABLE does.
16482 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16483 * constraints. As long as tables have more like 10 constraints it shouldn't be
16484 * a problem though. Even 100 constraints ought not be the end of the world.
16486 * XXX See MergeWithExistingConstraint too if you change this code.
16489 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
16491 Relation constraintrel
;
16492 SysScanDesc parent_scan
;
16493 ScanKeyData parent_key
;
16494 HeapTuple parent_tuple
;
16495 Oid parent_relid
= RelationGetRelid(parent_rel
);
16497 constraintrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16499 /* Outer loop scans through the parent's constraint definitions */
16500 ScanKeyInit(&parent_key
,
16501 Anum_pg_constraint_conrelid
,
16502 BTEqualStrategyNumber
, F_OIDEQ
,
16503 ObjectIdGetDatum(parent_relid
));
16504 parent_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16505 true, NULL
, 1, &parent_key
);
16507 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
16509 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
16510 SysScanDesc child_scan
;
16511 ScanKeyData child_key
;
16512 HeapTuple child_tuple
;
16513 bool found
= false;
16515 if (parent_con
->contype
!= CONSTRAINT_CHECK
&&
16516 parent_con
->contype
!= CONSTRAINT_NOTNULL
)
16519 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16520 if (parent_con
->connoinherit
)
16523 /* Search for a child constraint matching this one */
16524 ScanKeyInit(&child_key
,
16525 Anum_pg_constraint_conrelid
,
16526 BTEqualStrategyNumber
, F_OIDEQ
,
16527 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16528 child_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
16529 true, NULL
, 1, &child_key
);
16531 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
16533 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
16534 HeapTuple child_copy
;
16536 if (child_con
->contype
!= parent_con
->contype
)
16540 * CHECK constraint are matched by name, NOT NULL ones by
16543 if (child_con
->contype
== CONSTRAINT_CHECK
)
16545 if (strcmp(NameStr(parent_con
->conname
),
16546 NameStr(child_con
->conname
)) != 0)
16549 else if (child_con
->contype
== CONSTRAINT_NOTNULL
)
16551 AttrNumber parent_attno
= extractNotNullColumn(parent_tuple
);
16552 AttrNumber child_attno
= extractNotNullColumn(child_tuple
);
16554 if (strcmp(get_attname(parent_relid
, parent_attno
, false),
16555 get_attname(RelationGetRelid(child_rel
), child_attno
,
16560 if (child_con
->contype
== CONSTRAINT_CHECK
&&
16561 !constraints_equivalent(parent_tuple
, child_tuple
, RelationGetDescr(constraintrel
)))
16563 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16564 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16565 RelationGetRelationName(child_rel
), NameStr(parent_con
->conname
))));
16568 * If the CHECK child constraint is "no inherit" then cannot
16571 * This is not desirable for not-null constraints, mostly because
16572 * it breaks our pg_upgrade strategy, but it also makes sense on
16573 * its own: if a child has its own not-null constraint and then
16574 * acquires a parent with the same constraint, then we start to
16575 * enforce that constraint for all the descendants of that child
16578 if (child_con
->contype
== CONSTRAINT_CHECK
&&
16579 child_con
->connoinherit
)
16581 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16582 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16583 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16586 * If the child constraint is "not valid" then cannot merge with a
16587 * valid parent constraint
16589 if (parent_con
->convalidated
&& !child_con
->convalidated
)
16591 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
16592 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16593 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
16596 * OK, bump the child constraint's inheritance count. (If we fail
16597 * later on, this change will just roll back.)
16599 child_copy
= heap_copytuple(child_tuple
);
16600 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
16601 child_con
->coninhcount
++;
16602 if (child_con
->coninhcount
< 0)
16604 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
16605 errmsg("too many inheritance parents"));
16606 if (child_con
->contype
== CONSTRAINT_NOTNULL
&&
16607 child_con
->connoinherit
)
16608 child_con
->connoinherit
= false;
16611 * In case of partitions, an inherited constraint must be
16612 * inherited only once since it cannot have multiple parents and
16613 * it is never considered local.
16615 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
16617 Assert(child_con
->coninhcount
== 1);
16618 child_con
->conislocal
= false;
16621 CatalogTupleUpdate(constraintrel
, &child_copy
->t_self
, child_copy
);
16622 heap_freetuple(child_copy
);
16628 systable_endscan(child_scan
);
16632 if (parent_con
->contype
== CONSTRAINT_NOTNULL
)
16634 errcode(ERRCODE_DATATYPE_MISMATCH
),
16635 errmsg("column \"%s\" in child table must be marked NOT NULL",
16636 get_attname(parent_relid
,
16637 extractNotNullColumn(parent_tuple
),
16641 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16642 errmsg("child table is missing constraint \"%s\"",
16643 NameStr(parent_con
->conname
))));
16647 systable_endscan(parent_scan
);
16648 table_close(constraintrel
, RowExclusiveLock
);
16652 * ALTER TABLE NO INHERIT
16654 * Return value is the address of the relation that is no longer parent.
16656 static ObjectAddress
16657 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
16659 ObjectAddress address
;
16660 Relation parent_rel
;
16662 if (rel
->rd_rel
->relispartition
)
16664 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16665 errmsg("cannot change inheritance of a partition")));
16668 * AccessShareLock on the parent is probably enough, seeing that DROP
16669 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16670 * be inspecting the parent's schema.
16672 parent_rel
= table_openrv(parent
, AccessShareLock
);
16675 * We don't bother to check ownership of the parent table --- ownership of
16676 * the child is presumed enough rights.
16679 /* Off to RemoveInheritance() where most of the work happens */
16680 RemoveInheritance(rel
, parent_rel
, false);
16683 * If parent_rel has a primary key, then child_rel has not-null
16684 * constraints that make these columns as non nullable. Mark those
16685 * constraints as no longer inherited by this parent.
16687 ATInheritAdjustNotNulls(parent_rel
, rel
, -1);
16690 * If the parent has a primary key, then we decrement counts for all NOT
16694 ObjectAddressSet(address
, RelationRelationId
,
16695 RelationGetRelid(parent_rel
));
16697 /* keep our lock on the parent relation until commit */
16698 table_close(parent_rel
, NoLock
);
16704 * MarkInheritDetached
16706 * Set inhdetachpending for a partition, for ATExecDetachPartition
16707 * in concurrent mode. While at it, verify that no other partition is
16708 * already pending detach.
16711 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
16713 Relation catalogRelation
;
16716 HeapTuple inheritsTuple
;
16717 bool found
= false;
16719 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16722 * Find pg_inherits entries by inhparent. (We need to scan them all in
16723 * order to verify that no other partition is pending detach.)
16725 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16727 Anum_pg_inherits_inhparent
,
16728 BTEqualStrategyNumber
, F_OIDEQ
,
16729 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16730 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
16731 true, NULL
, 1, &key
);
16733 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16735 Form_pg_inherits inhForm
;
16737 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16738 if (inhForm
->inhdetachpending
)
16740 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16741 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16742 get_rel_name(inhForm
->inhrelid
),
16743 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
16744 RelationGetRelationName(parent_rel
)),
16745 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16747 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
16751 newtup
= heap_copytuple(inheritsTuple
);
16752 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
16754 CatalogTupleUpdate(catalogRelation
,
16755 &inheritsTuple
->t_self
,
16758 heap_freetuple(newtup
);
16759 /* keep looking, to ensure we catch others pending detach */
16764 systable_endscan(scan
);
16765 table_close(catalogRelation
, RowExclusiveLock
);
16769 (errcode(ERRCODE_UNDEFINED_TABLE
),
16770 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16771 RelationGetRelationName(child_rel
),
16772 RelationGetRelationName(parent_rel
))));
16776 * RemoveInheritance
16778 * Drop a parent from the child's parents. This just adjusts the attinhcount
16779 * and attislocal of the columns and removes the pg_inherit and pg_depend
16780 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16782 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16783 * up attislocal stays true, which means if a child is ever removed from a
16784 * parent then its columns will never be automatically dropped which may
16785 * surprise. But at least we'll never surprise by dropping columns someone
16786 * isn't expecting to be dropped which would actually mean data loss.
16788 * coninhcount and conislocal for inherited constraints are adjusted in
16789 * exactly the same way.
16791 * Common to ATExecDropInherit() and ATExecDetachPartition().
16794 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
16796 Relation catalogRelation
;
16798 ScanKeyData key
[3];
16799 HeapTuple attributeTuple
,
16804 bool is_partitioning
;
16806 is_partitioning
= (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16808 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
16809 RelationGetRelid(parent_rel
),
16811 RelationGetRelationName(child_rel
));
16814 if (is_partitioning
)
16816 (errcode(ERRCODE_UNDEFINED_TABLE
),
16817 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16818 RelationGetRelationName(child_rel
),
16819 RelationGetRelationName(parent_rel
))));
16822 (errcode(ERRCODE_UNDEFINED_TABLE
),
16823 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16824 RelationGetRelationName(parent_rel
),
16825 RelationGetRelationName(child_rel
))));
16829 * Search through child columns looking for ones matching parent rel
16831 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
16832 ScanKeyInit(&key
[0],
16833 Anum_pg_attribute_attrelid
,
16834 BTEqualStrategyNumber
, F_OIDEQ
,
16835 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16836 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
16837 true, NULL
, 1, key
);
16838 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
16840 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
16842 /* Ignore if dropped or not inherited */
16843 if (att
->attisdropped
)
16845 if (att
->attinhcount
<= 0)
16848 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
16849 NameStr(att
->attname
)))
16851 /* Decrement inhcount and possibly set islocal to true */
16852 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
16853 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
16855 copy_att
->attinhcount
--;
16856 if (copy_att
->attinhcount
== 0)
16857 copy_att
->attislocal
= true;
16859 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16860 heap_freetuple(copyTuple
);
16863 systable_endscan(scan
);
16864 table_close(catalogRelation
, RowExclusiveLock
);
16867 * Likewise, find inherited check constraints and disinherit them. To do
16868 * this, we first need a list of the names of the parent's check
16869 * constraints. (We cheat a bit by only checking for name matches,
16870 * assuming that the expressions will match.)
16872 * For NOT NULL columns, we store column numbers to match.
16874 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16875 ScanKeyInit(&key
[0],
16876 Anum_pg_constraint_conrelid
,
16877 BTEqualStrategyNumber
, F_OIDEQ
,
16878 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16879 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16880 true, NULL
, 1, key
);
16885 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16887 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16889 if (con
->contype
== CONSTRAINT_CHECK
)
16890 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
16891 if (con
->contype
== CONSTRAINT_NOTNULL
)
16892 nncolumns
= lappend_int(nncolumns
, extractNotNullColumn(constraintTuple
));
16895 systable_endscan(scan
);
16897 /* Now scan the child's constraints */
16898 ScanKeyInit(&key
[0],
16899 Anum_pg_constraint_conrelid
,
16900 BTEqualStrategyNumber
, F_OIDEQ
,
16901 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16902 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16903 true, NULL
, 1, key
);
16905 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16907 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16908 bool match
= false;
16912 * Match CHECK constraints by name, not-null constraints by column
16913 * number, and ignore all others.
16915 if (con
->contype
== CONSTRAINT_CHECK
)
16917 foreach(lc
, connames
)
16919 if (con
->contype
== CONSTRAINT_CHECK
&&
16920 strcmp(NameStr(con
->conname
), (char *) lfirst(lc
)) == 0)
16927 else if (con
->contype
== CONSTRAINT_NOTNULL
)
16929 AttrNumber child_attno
= extractNotNullColumn(constraintTuple
);
16931 foreach(lc
, nncolumns
)
16933 if (lfirst_int(lc
) == child_attno
)
16945 /* Decrement inhcount and possibly set islocal to true */
16946 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
16947 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
16949 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
16950 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
16951 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
16953 copy_con
->coninhcount
--;
16954 if (copy_con
->coninhcount
== 0)
16955 copy_con
->conislocal
= true;
16957 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16958 heap_freetuple(copyTuple
);
16962 systable_endscan(scan
);
16963 table_close(catalogRelation
, RowExclusiveLock
);
16965 drop_parent_dependency(RelationGetRelid(child_rel
),
16966 RelationRelationId
,
16967 RelationGetRelid(parent_rel
),
16968 child_dependency_type(is_partitioning
));
16971 * Post alter hook of this inherits. Since object_access_hook doesn't take
16972 * multiple object identifiers, we relay oid of parent relation using
16973 * auxiliary_id argument.
16975 InvokeObjectPostAlterHookArg(InheritsRelationId
,
16976 RelationGetRelid(child_rel
), 0,
16977 RelationGetRelid(parent_rel
), false);
16981 * Adjust coninhcount of not-null constraints upwards or downwards when a
16982 * table is marked as inheriting or no longer doing so a table with a primary
16985 * Note: these constraints are not dropped, even if their inhcount goes to zero
16986 * and conislocal is false. Instead we mark the constraints as locally defined.
16987 * This is seen as more useful behavior, with no downsides. The user can always
16988 * drop them afterwards.
16991 ATInheritAdjustNotNulls(Relation parent_rel
, Relation child_rel
, int inhcount
)
16993 Bitmapset
*pkattnos
;
16995 /* Quick exit when parent has no PK */
16996 if (!parent_rel
->rd_rel
->relhasindex
)
16999 pkattnos
= RelationGetIndexAttrBitmap(parent_rel
,
17000 INDEX_ATTR_BITMAP_PRIMARY_KEY
);
17001 if (pkattnos
!= NULL
)
17003 Bitmapset
*childattnums
= NULL
;
17007 attmap
= build_attrmap_by_name(RelationGetDescr(parent_rel
),
17008 RelationGetDescr(child_rel
), true);
17011 while ((i
= bms_next_member(pkattnos
, i
)) >= 0)
17013 childattnums
= bms_add_member(childattnums
,
17014 attmap
->attnums
[i
+ FirstLowInvalidHeapAttributeNumber
- 1]);
17018 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17019 * parent: the relevant not-null constraint in the child already had
17020 * its inhcount modified earlier.
17022 CommandCounterIncrement();
17023 AdjustNotNullInheritance(RelationGetRelid(child_rel
), childattnums
,
17029 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17030 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17031 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17032 * be TypeRelationId). There's no convenient way to do this, so go trawling
17033 * through pg_depend.
17036 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
17037 DependencyType deptype
)
17039 Relation catalogRelation
;
17041 ScanKeyData key
[3];
17042 HeapTuple depTuple
;
17044 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
17046 ScanKeyInit(&key
[0],
17047 Anum_pg_depend_classid
,
17048 BTEqualStrategyNumber
, F_OIDEQ
,
17049 ObjectIdGetDatum(RelationRelationId
));
17050 ScanKeyInit(&key
[1],
17051 Anum_pg_depend_objid
,
17052 BTEqualStrategyNumber
, F_OIDEQ
,
17053 ObjectIdGetDatum(relid
));
17054 ScanKeyInit(&key
[2],
17055 Anum_pg_depend_objsubid
,
17056 BTEqualStrategyNumber
, F_INT4EQ
,
17059 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
17062 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
17064 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
17066 if (dep
->refclassid
== refclassid
&&
17067 dep
->refobjid
== refobjid
&&
17068 dep
->refobjsubid
== 0 &&
17069 dep
->deptype
== deptype
)
17070 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
17073 systable_endscan(scan
);
17074 table_close(catalogRelation
, RowExclusiveLock
);
17080 * Attach a table to a composite type, as though it had been created with CREATE
17081 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17082 * subject table must not have inheritance parents. These restrictions ensure
17083 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17085 * The address of the type is returned.
17087 static ObjectAddress
17088 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
17090 Oid relid
= RelationGetRelid(rel
);
17092 Form_pg_type typeform
;
17094 Relation inheritsRelation
,
17098 AttrNumber table_attno
,
17100 TupleDesc typeTupleDesc
,
17102 ObjectAddress tableobj
,
17104 HeapTuple classtuple
;
17106 /* Validate the type. */
17107 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
17108 check_of_type(typetuple
);
17109 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
17110 typeid = typeform
->oid
;
17112 /* Fail if the table has any inheritance parents. */
17113 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
17115 Anum_pg_inherits_inhrelid
,
17116 BTEqualStrategyNumber
, F_OIDEQ
,
17117 ObjectIdGetDatum(relid
));
17118 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
17119 true, NULL
, 1, &key
);
17120 if (HeapTupleIsValid(systable_getnext(scan
)))
17122 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17123 errmsg("typed tables cannot inherit")));
17124 systable_endscan(scan
);
17125 table_close(inheritsRelation
, AccessShareLock
);
17128 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17129 * require that the order also match. However, attnotnull need not match.
17131 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
17132 tableTupleDesc
= RelationGetDescr(rel
);
17134 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
17136 Form_pg_attribute type_attr
,
17138 const char *type_attname
,
17141 /* Get the next non-dropped type attribute. */
17142 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
17143 if (type_attr
->attisdropped
)
17145 type_attname
= NameStr(type_attr
->attname
);
17147 /* Get the next non-dropped table attribute. */
17150 if (table_attno
> tableTupleDesc
->natts
)
17152 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17153 errmsg("table is missing column \"%s\"",
17155 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
17157 } while (table_attr
->attisdropped
);
17158 table_attname
= NameStr(table_attr
->attname
);
17160 /* Compare name. */
17161 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
17163 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17164 errmsg("table has column \"%s\" where type requires \"%s\"",
17165 table_attname
, type_attname
)));
17167 /* Compare type. */
17168 if (table_attr
->atttypid
!= type_attr
->atttypid
||
17169 table_attr
->atttypmod
!= type_attr
->atttypmod
||
17170 table_attr
->attcollation
!= type_attr
->attcollation
)
17172 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17173 errmsg("table \"%s\" has different type for column \"%s\"",
17174 RelationGetRelationName(rel
), type_attname
)));
17176 ReleaseTupleDesc(typeTupleDesc
);
17178 /* Any remaining columns at the end of the table had better be dropped. */
17179 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
17181 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
17184 if (!table_attr
->attisdropped
)
17186 (errcode(ERRCODE_DATATYPE_MISMATCH
),
17187 errmsg("table has extra column \"%s\"",
17188 NameStr(table_attr
->attname
))));
17191 /* If the table was already typed, drop the existing dependency. */
17192 if (rel
->rd_rel
->reloftype
)
17193 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17194 DEPENDENCY_NORMAL
);
17196 /* Record a dependency on the new type. */
17197 tableobj
.classId
= RelationRelationId
;
17198 tableobj
.objectId
= relid
;
17199 tableobj
.objectSubId
= 0;
17200 typeobj
.classId
= TypeRelationId
;
17201 typeobj
.objectId
= typeid;
17202 typeobj
.objectSubId
= 0;
17203 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
17205 /* Update pg_class.reloftype */
17206 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17207 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17208 if (!HeapTupleIsValid(classtuple
))
17209 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17210 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
17211 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
17213 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17215 heap_freetuple(classtuple
);
17216 table_close(relationRelation
, RowExclusiveLock
);
17218 ReleaseSysCache(typetuple
);
17224 * ALTER TABLE NOT OF
17226 * Detach a typed table from its originating type. Just clear reloftype and
17227 * remove the dependency.
17230 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
17232 Oid relid
= RelationGetRelid(rel
);
17233 Relation relationRelation
;
17236 if (!OidIsValid(rel
->rd_rel
->reloftype
))
17238 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17239 errmsg("\"%s\" is not a typed table",
17240 RelationGetRelationName(rel
))));
17243 * We don't bother to check ownership of the type --- ownership of the
17244 * table is presumed enough rights. No lock required on the type, either.
17247 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
17248 DEPENDENCY_NORMAL
);
17250 /* Clear pg_class.reloftype */
17251 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
17252 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17253 if (!HeapTupleIsValid(tuple
))
17254 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17255 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
17256 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
17258 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
17260 heap_freetuple(tuple
);
17261 table_close(relationRelation
, RowExclusiveLock
);
17265 * relation_mark_replica_identity: Update a table's replica identity
17267 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17268 * index. Otherwise, it must be InvalidOid.
17270 * Caller had better hold an exclusive lock on the relation, as the results
17271 * of running two of these concurrently wouldn't be pretty.
17274 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
17279 HeapTuple pg_class_tuple
;
17280 HeapTuple pg_index_tuple
;
17281 Form_pg_class pg_class_form
;
17282 Form_pg_index pg_index_form
;
17286 * Check whether relreplident has changed, and update it if so.
17288 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17289 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
17290 ObjectIdGetDatum(RelationGetRelid(rel
)));
17291 if (!HeapTupleIsValid(pg_class_tuple
))
17292 elog(ERROR
, "cache lookup failed for relation \"%s\"",
17293 RelationGetRelationName(rel
));
17294 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
17295 if (pg_class_form
->relreplident
!= ri_type
)
17297 pg_class_form
->relreplident
= ri_type
;
17298 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
17300 table_close(pg_class
, RowExclusiveLock
);
17301 heap_freetuple(pg_class_tuple
);
17304 * Update the per-index indisreplident flags correctly.
17306 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
17307 foreach(index
, RelationGetIndexList(rel
))
17309 Oid thisIndexOid
= lfirst_oid(index
);
17310 bool dirty
= false;
17312 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
17313 ObjectIdGetDatum(thisIndexOid
));
17314 if (!HeapTupleIsValid(pg_index_tuple
))
17315 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
17316 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
17318 if (thisIndexOid
== indexOid
)
17320 /* Set the bit if not already set. */
17321 if (!pg_index_form
->indisreplident
)
17324 pg_index_form
->indisreplident
= true;
17329 /* Unset the bit if set. */
17330 if (pg_index_form
->indisreplident
)
17333 pg_index_form
->indisreplident
= false;
17339 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
17340 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
17341 InvalidOid
, is_internal
);
17344 * Invalidate the relcache for the table, so that after we commit
17345 * all sessions will refresh the table's replica identity index
17346 * before attempting any UPDATE or DELETE on the table. (If we
17347 * changed the table's pg_class row above, then a relcache inval
17348 * is already queued due to that; but we might not have.)
17350 CacheInvalidateRelcache(rel
);
17352 heap_freetuple(pg_index_tuple
);
17355 table_close(pg_index
, RowExclusiveLock
);
17359 * ALTER TABLE <name> REPLICA IDENTITY ...
17362 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
17368 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
17370 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17373 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
17375 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17378 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
17380 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
17383 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
17385 /* fallthrough */ ;
17388 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
17390 /* Check that the index exists */
17391 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
17392 if (!OidIsValid(indexOid
))
17394 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17395 errmsg("index \"%s\" for table \"%s\" does not exist",
17396 stmt
->name
, RelationGetRelationName(rel
))));
17398 indexRel
= index_open(indexOid
, ShareLock
);
17400 /* Check that the index is on the relation we're altering. */
17401 if (indexRel
->rd_index
== NULL
||
17402 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
17404 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17405 errmsg("\"%s\" is not an index for table \"%s\"",
17406 RelationGetRelationName(indexRel
),
17407 RelationGetRelationName(rel
))));
17408 /* The AM must support uniqueness, and the index must in fact be unique. */
17409 if (!indexRel
->rd_indam
->amcanunique
||
17410 !indexRel
->rd_index
->indisunique
)
17412 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17413 errmsg("cannot use non-unique index \"%s\" as replica identity",
17414 RelationGetRelationName(indexRel
))));
17415 /* Deferred indexes are not guaranteed to be always unique. */
17416 if (!indexRel
->rd_index
->indimmediate
)
17418 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17419 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17420 RelationGetRelationName(indexRel
))));
17421 /* Expression indexes aren't supported. */
17422 if (RelationGetIndexExpressions(indexRel
) != NIL
)
17424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17425 errmsg("cannot use expression index \"%s\" as replica identity",
17426 RelationGetRelationName(indexRel
))));
17427 /* Predicate indexes aren't supported. */
17428 if (RelationGetIndexPredicate(indexRel
) != NIL
)
17430 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17431 errmsg("cannot use partial index \"%s\" as replica identity",
17432 RelationGetRelationName(indexRel
))));
17434 /* Check index for nullable columns. */
17435 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
17437 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
17438 Form_pg_attribute attr
;
17441 * Reject any other system columns. (Going forward, we'll disallow
17442 * indexes containing such columns in the first place, but they might
17443 * exist in older branches.)
17447 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
17448 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17449 RelationGetRelationName(indexRel
), attno
)));
17451 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
17452 if (!attr
->attnotnull
)
17454 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17455 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17456 RelationGetRelationName(indexRel
),
17457 NameStr(attr
->attname
))));
17460 /* This index is suitable for use as a replica identity. Mark it. */
17461 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
17463 index_close(indexRel
, NoLock
);
17467 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17470 ATExecSetRowSecurity(Relation rel
, bool rls
)
17476 relid
= RelationGetRelid(rel
);
17478 /* Pull the record for this relation and update it */
17479 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17481 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17483 if (!HeapTupleIsValid(tuple
))
17484 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17486 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
17487 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17489 InvokeObjectPostAlterHook(RelationRelationId
,
17490 RelationGetRelid(rel
), 0);
17492 table_close(pg_class
, RowExclusiveLock
);
17493 heap_freetuple(tuple
);
17497 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17500 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
17506 relid
= RelationGetRelid(rel
);
17508 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
17510 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
17512 if (!HeapTupleIsValid(tuple
))
17513 elog(ERROR
, "cache lookup failed for relation %u", relid
);
17515 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
17516 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
17518 InvokeObjectPostAlterHook(RelationRelationId
,
17519 RelationGetRelid(rel
), 0);
17521 table_close(pg_class
, RowExclusiveLock
);
17522 heap_freetuple(tuple
);
17526 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17529 ATExecGenericOptions(Relation rel
, List
*options
)
17532 ForeignServer
*server
;
17533 ForeignDataWrapper
*fdw
;
17536 Datum repl_val
[Natts_pg_foreign_table
];
17537 bool repl_null
[Natts_pg_foreign_table
];
17538 bool repl_repl
[Natts_pg_foreign_table
];
17540 Form_pg_foreign_table tableform
;
17542 if (options
== NIL
)
17545 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
17547 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
17548 ObjectIdGetDatum(rel
->rd_id
));
17549 if (!HeapTupleIsValid(tuple
))
17551 (errcode(ERRCODE_UNDEFINED_OBJECT
),
17552 errmsg("foreign table \"%s\" does not exist",
17553 RelationGetRelationName(rel
))));
17554 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
17555 server
= GetForeignServer(tableform
->ftserver
);
17556 fdw
= GetForeignDataWrapper(server
->fdwid
);
17558 memset(repl_val
, 0, sizeof(repl_val
));
17559 memset(repl_null
, false, sizeof(repl_null
));
17560 memset(repl_repl
, false, sizeof(repl_repl
));
17562 /* Extract the current options */
17563 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
17565 Anum_pg_foreign_table_ftoptions
,
17568 datum
= PointerGetDatum(NULL
);
17570 /* Transform the options */
17571 datum
= transformGenericOptions(ForeignTableRelationId
,
17574 fdw
->fdwvalidator
);
17576 if (PointerIsValid(DatumGetPointer(datum
)))
17577 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
17579 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17581 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
17583 /* Everything looks good - update the tuple */
17585 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
17586 repl_val
, repl_null
, repl_repl
);
17588 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
17591 * Invalidate relcache so that all sessions will refresh any cached plans
17592 * that might depend on the old options.
17594 CacheInvalidateRelcache(rel
);
17596 InvokeObjectPostAlterHook(ForeignTableRelationId
,
17597 RelationGetRelid(rel
), 0);
17599 table_close(ftrel
, RowExclusiveLock
);
17601 heap_freetuple(tuple
);
17605 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17607 * Return value is the address of the modified column
17609 static ObjectAddress
17610 ATExecSetCompression(Relation rel
,
17611 const char *column
,
17617 Form_pg_attribute atttableform
;
17621 ObjectAddress address
;
17623 compression
= strVal(newValue
);
17625 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
17627 /* copy the cache entry so we can scribble on it below */
17628 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
17629 if (!HeapTupleIsValid(tuple
))
17631 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17632 errmsg("column \"%s\" of relation \"%s\" does not exist",
17633 column
, RelationGetRelationName(rel
))));
17635 /* prevent them from altering a system attribute */
17636 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
17637 attnum
= atttableform
->attnum
;
17640 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17641 errmsg("cannot alter system column \"%s\"", column
)));
17644 * Check that column type is compressible, then get the attribute
17645 * compression method code
17647 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
17649 /* update pg_attribute entry */
17650 atttableform
->attcompression
= cmethod
;
17651 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
17653 InvokeObjectPostAlterHook(RelationRelationId
,
17654 RelationGetRelid(rel
),
17658 * Apply the change to indexes as well (only for simple index columns,
17659 * matching behavior of index.c ConstructTupleDescriptor()).
17661 SetIndexStorageProperties(rel
, attrel
, attnum
,
17666 heap_freetuple(tuple
);
17668 table_close(attrel
, RowExclusiveLock
);
17670 /* make changes visible */
17671 CommandCounterIncrement();
17673 ObjectAddressSubSet(address
, RelationRelationId
,
17674 RelationGetRelid(rel
), attnum
);
17680 * Preparation phase for SET LOGGED/UNLOGGED
17682 * This verifies that we're not trying to change a temp table. Also,
17683 * existing foreign key constraints are checked to avoid ending up with
17684 * permanent tables referencing unlogged tables.
17686 * Return value is false if the operation is a no-op (in which case the
17687 * checks are skipped), otherwise true.
17690 ATPrepChangePersistence(Relation rel
, bool toLogged
)
17692 Relation pg_constraint
;
17695 ScanKeyData skey
[1];
17698 * Disallow changing status for a temp table. Also verify whether we can
17699 * get away with doing nothing; in such cases we don't need to run the
17700 * checks below, either.
17702 switch (rel
->rd_rel
->relpersistence
)
17704 case RELPERSISTENCE_TEMP
:
17706 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17707 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17708 RelationGetRelationName(rel
)),
17711 case RELPERSISTENCE_PERMANENT
:
17713 /* nothing to do */
17716 case RELPERSISTENCE_UNLOGGED
:
17718 /* nothing to do */
17724 * Check that the table is not part of any publication when changing to
17725 * UNLOGGED, as UNLOGGED tables can't be published.
17728 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
17730 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
17731 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17732 RelationGetRelationName(rel
)),
17733 errdetail("Unlogged relations cannot be replicated.")));
17736 * Check existing foreign key constraints to preserve the invariant that
17737 * permanent tables cannot reference unlogged ones. Self-referencing
17738 * foreign keys can safely be ignored.
17740 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
17743 * Scan conrelid if changing to permanent, else confrelid. This also
17744 * determines whether a useful index exists.
17746 ScanKeyInit(&skey
[0],
17747 toLogged
? Anum_pg_constraint_conrelid
:
17748 Anum_pg_constraint_confrelid
,
17749 BTEqualStrategyNumber
, F_OIDEQ
,
17750 ObjectIdGetDatum(RelationGetRelid(rel
)));
17751 scan
= systable_beginscan(pg_constraint
,
17752 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
17753 true, NULL
, 1, skey
);
17755 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
17757 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
17759 if (con
->contype
== CONSTRAINT_FOREIGN
)
17762 Relation foreignrel
;
17764 /* the opposite end of what we used as scankey */
17765 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
17767 /* ignore if self-referencing */
17768 if (RelationGetRelid(rel
) == foreignrelid
)
17771 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
17775 if (!RelationIsPermanent(foreignrel
))
17777 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17778 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17779 RelationGetRelationName(rel
),
17780 RelationGetRelationName(foreignrel
)),
17781 errtableconstraint(rel
, NameStr(con
->conname
))));
17785 if (RelationIsPermanent(foreignrel
))
17787 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17788 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17789 RelationGetRelationName(rel
),
17790 RelationGetRelationName(foreignrel
)),
17791 errtableconstraint(rel
, NameStr(con
->conname
))));
17794 relation_close(foreignrel
, AccessShareLock
);
17798 systable_endscan(scan
);
17800 table_close(pg_constraint
, AccessShareLock
);
17806 * Execute ALTER TABLE SET SCHEMA
17809 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
17816 ObjectAddresses
*objsMoved
;
17817 ObjectAddress myself
;
17819 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
17820 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
17821 RangeVarCallbackForAlterRelation
,
17824 if (!OidIsValid(relid
))
17827 (errmsg("relation \"%s\" does not exist, skipping",
17828 stmt
->relation
->relname
)));
17829 return InvalidObjectAddress
;
17832 rel
= relation_open(relid
, NoLock
);
17834 oldNspOid
= RelationGetNamespace(rel
);
17836 /* If it's an owned sequence, disallow moving it by itself. */
17837 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
17842 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
17843 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
17845 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17846 errmsg("cannot move an owned sequence into another schema"),
17847 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17848 RelationGetRelationName(rel
),
17849 get_rel_name(tableId
))));
17852 /* Get and lock schema OID and check its permissions. */
17853 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
17854 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
17856 /* common checks on switching namespaces */
17857 CheckSetNamespace(oldNspOid
, nspOid
);
17859 objsMoved
= new_object_addresses();
17860 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
17861 free_object_addresses(objsMoved
);
17863 ObjectAddressSet(myself
, RelationRelationId
, relid
);
17866 *oldschema
= oldNspOid
;
17868 /* close rel, but keep lock until commit */
17869 relation_close(rel
, NoLock
);
17875 * The guts of relocating a table or materialized view to another namespace:
17876 * besides moving the relation itself, its dependent objects are relocated to
17880 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
17881 ObjectAddresses
*objsMoved
)
17885 Assert(objsMoved
!= NULL
);
17887 /* OK, modify the pg_class row and pg_depend entry */
17888 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
17890 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
17891 nspOid
, true, objsMoved
);
17893 /* Fix the table's row type too, if it has one */
17894 if (OidIsValid(rel
->rd_rel
->reltype
))
17895 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
,
17896 nspOid
, false, false, objsMoved
);
17898 /* Fix other dependent stuff */
17899 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
||
17900 rel
->rd_rel
->relkind
== RELKIND_MATVIEW
||
17901 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
17903 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
17904 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
17905 objsMoved
, AccessExclusiveLock
);
17906 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
17910 table_close(classRel
, RowExclusiveLock
);
17914 * The guts of relocating a relation to another namespace: fix the pg_class
17915 * entry, and the pg_depend entry if any. Caller must already have
17916 * opened and write-locked pg_class.
17919 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
17920 Oid oldNspOid
, Oid newNspOid
,
17921 bool hasDependEntry
,
17922 ObjectAddresses
*objsMoved
)
17924 HeapTuple classTup
;
17925 Form_pg_class classForm
;
17926 ObjectAddress thisobj
;
17927 bool already_done
= false;
17929 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relOid
));
17930 if (!HeapTupleIsValid(classTup
))
17931 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
17932 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
17934 Assert(classForm
->relnamespace
== oldNspOid
);
17936 thisobj
.classId
= RelationRelationId
;
17937 thisobj
.objectId
= relOid
;
17938 thisobj
.objectSubId
= 0;
17941 * If the object has already been moved, don't move it again. If it's
17942 * already in the right place, don't move it, but still fire the object
17945 already_done
= object_address_present(&thisobj
, objsMoved
);
17946 if (!already_done
&& oldNspOid
!= newNspOid
)
17948 /* check for duplicate name (more friendly than unique-index failure) */
17949 if (get_relname_relid(NameStr(classForm
->relname
),
17950 newNspOid
) != InvalidOid
)
17952 (errcode(ERRCODE_DUPLICATE_TABLE
),
17953 errmsg("relation \"%s\" already exists in schema \"%s\"",
17954 NameStr(classForm
->relname
),
17955 get_namespace_name(newNspOid
))));
17957 /* classTup is a copy, so OK to scribble on */
17958 classForm
->relnamespace
= newNspOid
;
17960 CatalogTupleUpdate(classRel
, &classTup
->t_self
, classTup
);
17962 /* Update dependency on schema if caller said so */
17963 if (hasDependEntry
&&
17964 changeDependencyFor(RelationRelationId
,
17966 NamespaceRelationId
,
17969 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
17970 NameStr(classForm
->relname
));
17974 add_exact_object_address(&thisobj
, objsMoved
);
17976 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
17979 heap_freetuple(classTup
);
17983 * Move all indexes for the specified relation to another namespace.
17985 * Note: we assume adequate permission checking was done by the caller,
17986 * and that the caller has a suitable lock on the owning relation.
17989 AlterIndexNamespaces(Relation classRel
, Relation rel
,
17990 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
17995 indexList
= RelationGetIndexList(rel
);
17997 foreach(l
, indexList
)
17999 Oid indexOid
= lfirst_oid(l
);
18000 ObjectAddress thisobj
;
18002 thisobj
.classId
= RelationRelationId
;
18003 thisobj
.objectId
= indexOid
;
18004 thisobj
.objectSubId
= 0;
18007 * Note: currently, the index will not have its own dependency on the
18008 * namespace, so we don't need to do changeDependencyFor(). There's no
18009 * row type in pg_type, either.
18011 * XXX this objsMoved test may be pointless -- surely we have a single
18012 * dependency link from a relation to each index?
18014 if (!object_address_present(&thisobj
, objsMoved
))
18016 AlterRelationNamespaceInternal(classRel
, indexOid
,
18017 oldNspOid
, newNspOid
,
18019 add_exact_object_address(&thisobj
, objsMoved
);
18023 list_free(indexList
);
18027 * Move all identity and SERIAL-column sequences of the specified relation to another
18030 * Note: we assume adequate permission checking was done by the caller,
18031 * and that the caller has a suitable lock on the owning relation.
18034 AlterSeqNamespaces(Relation classRel
, Relation rel
,
18035 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
18040 ScanKeyData key
[2];
18044 * SERIAL sequences are those having an auto dependency on one of the
18045 * table's columns (we don't care *which* column, exactly).
18047 depRel
= table_open(DependRelationId
, AccessShareLock
);
18049 ScanKeyInit(&key
[0],
18050 Anum_pg_depend_refclassid
,
18051 BTEqualStrategyNumber
, F_OIDEQ
,
18052 ObjectIdGetDatum(RelationRelationId
));
18053 ScanKeyInit(&key
[1],
18054 Anum_pg_depend_refobjid
,
18055 BTEqualStrategyNumber
, F_OIDEQ
,
18056 ObjectIdGetDatum(RelationGetRelid(rel
)));
18057 /* we leave refobjsubid unspecified */
18059 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
18062 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
18064 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
18067 /* skip dependencies other than auto dependencies on columns */
18068 if (depForm
->refobjsubid
== 0 ||
18069 depForm
->classid
!= RelationRelationId
||
18070 depForm
->objsubid
!= 0 ||
18071 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
18074 /* Use relation_open just in case it's an index */
18075 seqRel
= relation_open(depForm
->objid
, lockmode
);
18077 /* skip non-sequence relations */
18078 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
18080 /* No need to keep the lock */
18081 relation_close(seqRel
, lockmode
);
18085 /* Fix the pg_class and pg_depend entries */
18086 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
18087 oldNspOid
, newNspOid
,
18091 * Sequences used to have entries in pg_type, but no longer do. If we
18092 * ever re-instate that, we'll need to move the pg_type entry to the
18093 * new namespace, too (using AlterTypeNamespaceInternal).
18095 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
18097 /* Now we can close it. Keep the lock till end of transaction. */
18098 relation_close(seqRel
, NoLock
);
18101 systable_endscan(scan
);
18103 relation_close(depRel
, AccessShareLock
);
18108 * This code supports
18109 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18111 * Because we only support this for TEMP tables, it's sufficient to remember
18112 * the state in a backend-local data structure.
18116 * Register a newly-created relation's ON COMMIT action.
18119 register_on_commit_action(Oid relid
, OnCommitAction action
)
18122 MemoryContext oldcxt
;
18125 * We needn't bother registering the relation unless there is an ON COMMIT
18126 * action we need to take.
18128 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
18131 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
18133 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
18135 oc
->oncommit
= action
;
18136 oc
->creating_subid
= GetCurrentSubTransactionId();
18137 oc
->deleting_subid
= InvalidSubTransactionId
;
18140 * We use lcons() here so that ON COMMIT actions are processed in reverse
18141 * order of registration. That might not be essential but it seems
18144 on_commits
= lcons(oc
, on_commits
);
18146 MemoryContextSwitchTo(oldcxt
);
18150 * Unregister any ON COMMIT action when a relation is deleted.
18152 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18155 remove_on_commit_action(Oid relid
)
18159 foreach(l
, on_commits
)
18161 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18163 if (oc
->relid
== relid
)
18165 oc
->deleting_subid
= GetCurrentSubTransactionId();
18172 * Perform ON COMMIT actions.
18174 * This is invoked just before actually committing, since it's possible
18175 * to encounter errors.
18178 PreCommit_on_commit_actions(void)
18181 List
*oids_to_truncate
= NIL
;
18182 List
*oids_to_drop
= NIL
;
18184 foreach(l
, on_commits
)
18186 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18188 /* Ignore entry if already dropped in this xact */
18189 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
18192 switch (oc
->oncommit
)
18194 case ONCOMMIT_NOOP
:
18195 case ONCOMMIT_PRESERVE_ROWS
:
18196 /* Do nothing (there shouldn't be such entries, actually) */
18198 case ONCOMMIT_DELETE_ROWS
:
18201 * If this transaction hasn't accessed any temporary
18202 * relations, we can skip truncating ON COMMIT DELETE ROWS
18203 * tables, as they must still be empty.
18205 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
18206 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
18208 case ONCOMMIT_DROP
:
18209 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
18215 * Truncate relations before dropping so that all dependencies between
18216 * relations are removed after they are worked on. Doing it like this
18217 * might be a waste as it is possible that a relation being truncated will
18218 * be dropped anyway due to its parent being dropped, but this makes the
18219 * code more robust because of not having to re-check that the relation
18220 * exists at truncation time.
18222 if (oids_to_truncate
!= NIL
)
18223 heap_truncate(oids_to_truncate
);
18225 if (oids_to_drop
!= NIL
)
18227 ObjectAddresses
*targetObjects
= new_object_addresses();
18229 foreach(l
, oids_to_drop
)
18231 ObjectAddress object
;
18233 object
.classId
= RelationRelationId
;
18234 object
.objectId
= lfirst_oid(l
);
18235 object
.objectSubId
= 0;
18237 Assert(!object_address_present(&object
, targetObjects
));
18239 add_exact_object_address(&object
, targetObjects
);
18243 * Object deletion might involve toast table access (to clean up
18244 * toasted catalog entries), so ensure we have a valid snapshot.
18246 PushActiveSnapshot(GetTransactionSnapshot());
18249 * Since this is an automatic drop, rather than one directly initiated
18250 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18252 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
18253 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
18255 PopActiveSnapshot();
18257 #ifdef USE_ASSERT_CHECKING
18260 * Note that table deletion will call remove_on_commit_action, so the
18261 * entry should get marked as deleted.
18263 foreach(l
, on_commits
)
18265 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
18267 if (oc
->oncommit
!= ONCOMMIT_DROP
)
18270 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
18277 * Post-commit or post-abort cleanup for ON COMMIT management.
18279 * All we do here is remove no-longer-needed OnCommitItem entries.
18281 * During commit, remove entries that were deleted during this transaction;
18282 * during abort, remove those created during this transaction.
18285 AtEOXact_on_commit_actions(bool isCommit
)
18287 ListCell
*cur_item
;
18289 foreach(cur_item
, on_commits
)
18291 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18293 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
18294 oc
->creating_subid
!= InvalidSubTransactionId
)
18296 /* cur_item must be removed */
18297 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18302 /* cur_item must be preserved */
18303 oc
->creating_subid
= InvalidSubTransactionId
;
18304 oc
->deleting_subid
= InvalidSubTransactionId
;
18310 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18312 * During subabort, we can immediately remove entries created during this
18313 * subtransaction. During subcommit, just relabel entries marked during
18314 * this subtransaction as being the parent's responsibility.
18317 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
18318 SubTransactionId parentSubid
)
18320 ListCell
*cur_item
;
18322 foreach(cur_item
, on_commits
)
18324 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
18326 if (!isCommit
&& oc
->creating_subid
== mySubid
)
18328 /* cur_item must be removed */
18329 on_commits
= foreach_delete_current(on_commits
, cur_item
);
18334 /* cur_item must be preserved */
18335 if (oc
->creating_subid
== mySubid
)
18336 oc
->creating_subid
= parentSubid
;
18337 if (oc
->deleting_subid
== mySubid
)
18338 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
18344 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18345 * the relation to be locked only if (1) it's a plain or partitioned table,
18346 * materialized view, or TOAST table and (2) the current user is the owner (or
18347 * the superuser) or has been granted MAINTAIN. This meets the
18348 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18349 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18352 RangeVarCallbackMaintainsTable(const RangeVar
*relation
,
18353 Oid relId
, Oid oldRelId
, void *arg
)
18356 AclResult aclresult
;
18358 /* Nothing to do if the relation was not found. */
18359 if (!OidIsValid(relId
))
18363 * If the relation does exist, check whether it's an index. But note that
18364 * the relation might have been dropped between the time we did the name
18365 * lookup and now. In that case, there's nothing to do.
18367 relkind
= get_rel_relkind(relId
);
18370 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
18371 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
18373 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18374 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
18376 /* Check permissions */
18377 aclresult
= pg_class_aclcheck(relId
, GetUserId(), ACL_MAINTAIN
);
18378 if (aclresult
!= ACLCHECK_OK
)
18379 aclcheck_error(aclresult
,
18380 get_relkind_objtype(get_rel_relkind(relId
)),
18381 relation
->relname
);
18385 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18388 RangeVarCallbackForTruncate(const RangeVar
*relation
,
18389 Oid relId
, Oid oldRelId
, void *arg
)
18393 /* Nothing to do if the relation was not found. */
18394 if (!OidIsValid(relId
))
18397 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18398 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18399 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18401 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18402 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
18404 ReleaseSysCache(tuple
);
18408 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18409 * the owner of the relation, or superuser.
18412 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
18413 Oid relId
, Oid oldRelId
, void *arg
)
18417 /* Nothing to do if the relation was not found. */
18418 if (!OidIsValid(relId
))
18421 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
18422 if (!HeapTupleIsValid(tuple
)) /* should not happen */
18423 elog(ERROR
, "cache lookup failed for relation %u", relId
);
18425 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
18426 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
18427 relation
->relname
);
18429 if (!allowSystemTableMods
&&
18430 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
18432 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18433 errmsg("permission denied: \"%s\" is a system catalog",
18434 relation
->relname
)));
18436 ReleaseSysCache(tuple
);
18440 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18444 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
18447 Node
*stmt
= (Node
*) arg
;
18448 ObjectType reltype
;
18450 Form_pg_class classform
;
18451 AclResult aclresult
;
18454 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
18455 if (!HeapTupleIsValid(tuple
))
18456 return; /* concurrently dropped */
18457 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
18458 relkind
= classform
->relkind
;
18460 /* Must own relation. */
18461 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
18462 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
18464 /* No system table modifications unless explicitly allowed. */
18465 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
18467 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
18468 errmsg("permission denied: \"%s\" is a system catalog",
18472 * Extract the specified relation type from the statement parse tree.
18474 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18475 * have CREATE rights on the containing namespace.
18477 if (IsA(stmt
, RenameStmt
))
18479 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
18480 GetUserId(), ACL_CREATE
);
18481 if (aclresult
!= ACLCHECK_OK
)
18482 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
18483 get_namespace_name(classform
->relnamespace
));
18484 reltype
= ((RenameStmt
*) stmt
)->renameType
;
18486 else if (IsA(stmt
, AlterObjectSchemaStmt
))
18487 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
18489 else if (IsA(stmt
, AlterTableStmt
))
18490 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
18493 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
18494 reltype
= OBJECT_TABLE
; /* placate compiler */
18498 * For compatibility with prior releases, we allow ALTER TABLE to be used
18499 * with most other types of relations (but not composite types). We allow
18500 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18501 * otherwise. Otherwise, the user must select the correct form of the
18502 * command for the relation at issue.
18504 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
18506 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18507 errmsg("\"%s\" is not a sequence", rv
->relname
)));
18509 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
18511 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18512 errmsg("\"%s\" is not a view", rv
->relname
)));
18514 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
18516 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18517 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
18519 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
18521 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18522 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
18524 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
18526 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18527 errmsg("\"%s\" is not a composite type", rv
->relname
)));
18529 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
18530 relkind
!= RELKIND_PARTITIONED_INDEX
18531 && !IsA(stmt
, RenameStmt
))
18533 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18534 errmsg("\"%s\" is not an index", rv
->relname
)));
18537 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18540 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
18542 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18543 errmsg("\"%s\" is a composite type", rv
->relname
),
18544 /* translator: %s is an SQL ALTER command */
18545 errhint("Use %s instead.",
18549 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18550 * to a different schema, such as indexes and TOAST tables.
18552 if (IsA(stmt
, AlterObjectSchemaStmt
))
18554 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
18556 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18557 errmsg("cannot change schema of index \"%s\"",
18559 errhint("Change the schema of the table instead.")));
18560 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
18562 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18563 errmsg("cannot change schema of composite type \"%s\"",
18565 /* translator: %s is an SQL ALTER command */
18566 errhint("Use %s instead.",
18568 else if (relkind
== RELKIND_TOASTVALUE
)
18570 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18571 errmsg("cannot change schema of TOAST table \"%s\"",
18573 errhint("Change the schema of the table instead.")));
18576 ReleaseSysCache(tuple
);
18580 * Transform any expressions present in the partition key
18582 * Returns a transformed PartitionSpec.
18584 static PartitionSpec
*
18585 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
18587 PartitionSpec
*newspec
;
18588 ParseState
*pstate
;
18589 ParseNamespaceItem
*nsitem
;
18592 newspec
= makeNode(PartitionSpec
);
18594 newspec
->strategy
= partspec
->strategy
;
18595 newspec
->partParams
= NIL
;
18596 newspec
->location
= partspec
->location
;
18598 /* Check valid number of columns for strategy */
18599 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
18600 list_length(partspec
->partParams
) != 1)
18602 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18603 errmsg("cannot use \"list\" partition strategy with more than one column")));
18606 * Create a dummy ParseState and insert the target relation as its sole
18607 * rangetable entry. We need a ParseState for transformExpr.
18609 pstate
= make_parsestate(NULL
);
18610 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
18611 NULL
, false, true);
18612 addNSItemToQuery(pstate
, nsitem
, true, true, true);
18614 /* take care of any partition expressions */
18615 foreach(l
, partspec
->partParams
)
18617 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
18621 /* Copy, to avoid scribbling on the input */
18622 pelem
= copyObject(pelem
);
18624 /* Now do parse transformation of the expression */
18625 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
18626 EXPR_KIND_PARTITION_EXPRESSION
);
18628 /* we have to fix its collations too */
18629 assign_expr_collations(pstate
, pelem
->expr
);
18632 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
18639 * Compute per-partition-column information from a list of PartitionElems.
18640 * Expressions in the PartitionElems must be parse-analyzed already.
18643 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
18644 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
18645 PartitionStrategy strategy
)
18652 foreach(lc
, partParams
)
18654 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
18658 if (pelem
->name
!= NULL
)
18660 /* Simple attribute reference */
18661 HeapTuple atttuple
;
18662 Form_pg_attribute attform
;
18664 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
18666 if (!HeapTupleIsValid(atttuple
))
18668 (errcode(ERRCODE_UNDEFINED_COLUMN
),
18669 errmsg("column \"%s\" named in partition key does not exist",
18671 parser_errposition(pstate
, pelem
->location
)));
18672 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
18674 if (attform
->attnum
<= 0)
18676 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18677 errmsg("cannot use system column \"%s\" in partition key",
18679 parser_errposition(pstate
, pelem
->location
)));
18682 * Generated columns cannot work: They are computed after BEFORE
18683 * triggers, but partition routing is done before all triggers.
18685 if (attform
->attgenerated
)
18687 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18688 errmsg("cannot use generated column in partition key"),
18689 errdetail("Column \"%s\" is a generated column.",
18691 parser_errposition(pstate
, pelem
->location
)));
18693 partattrs
[attn
] = attform
->attnum
;
18694 atttype
= attform
->atttypid
;
18695 attcollation
= attform
->attcollation
;
18696 ReleaseSysCache(atttuple
);
18701 Node
*expr
= pelem
->expr
;
18702 char partattname
[16];
18704 Assert(expr
!= NULL
);
18705 atttype
= exprType(expr
);
18706 attcollation
= exprCollation(expr
);
18709 * The expression must be of a storable type (e.g., not RECORD).
18710 * The test is the same as for whether a table column is of a safe
18711 * type (which is why we needn't check for the non-expression
18714 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
18715 CheckAttributeType(partattname
,
18716 atttype
, attcollation
,
18717 NIL
, CHKATYPE_IS_PARTKEY
);
18720 * Strip any top-level COLLATE clause. This ensures that we treat
18721 * "x COLLATE y" and "(x COLLATE y)" alike.
18723 while (IsA(expr
, CollateExpr
))
18724 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
18726 if (IsA(expr
, Var
) &&
18727 ((Var
*) expr
)->varattno
> 0)
18730 * User wrote "(column)" or "(column COLLATE something)".
18731 * Treat it like simple attribute anyway.
18733 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
18737 Bitmapset
*expr_attrs
= NULL
;
18740 partattrs
[attn
] = 0; /* marks the column as expression */
18741 *partexprs
= lappend(*partexprs
, expr
);
18744 * transformPartitionSpec() should have already rejected
18745 * subqueries, aggregates, window functions, and SRFs, based
18746 * on the EXPR_KIND_ for partition expressions.
18750 * Cannot allow system column references, since that would
18751 * make partition routing impossible: their values won't be
18752 * known yet when we need to do that.
18754 pull_varattnos(expr
, 1, &expr_attrs
);
18755 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
18757 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
18760 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18761 errmsg("partition key expressions cannot contain system column references")));
18765 * Generated columns cannot work: They are computed after
18766 * BEFORE triggers, but partition routing is done before all
18770 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
18772 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
18775 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
18777 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18778 errmsg("cannot use generated column in partition key"),
18779 errdetail("Column \"%s\" is a generated column.",
18780 get_attname(RelationGetRelid(rel
), attno
, false)),
18781 parser_errposition(pstate
, pelem
->location
)));
18785 * Preprocess the expression before checking for mutability.
18786 * This is essential for the reasons described in
18787 * contain_mutable_functions_after_planning. However, we call
18788 * expression_planner for ourselves rather than using that
18789 * function, because if constant-folding reduces the
18790 * expression to a constant, we'd like to know that so we can
18793 * Like contain_mutable_functions_after_planning, assume that
18794 * expression_planner won't scribble on its input, so this
18795 * won't affect the partexprs entry we saved above.
18797 expr
= (Node
*) expression_planner((Expr
*) expr
);
18800 * Partition expressions cannot contain mutable functions,
18801 * because a given row must always map to the same partition
18802 * as long as there is no change in the partition boundary
18805 if (contain_mutable_functions(expr
))
18807 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18808 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18811 * While it is not exactly *wrong* for a partition expression
18812 * to be a constant, it seems better to reject such keys.
18814 if (IsA(expr
, Const
))
18816 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18817 errmsg("cannot use constant expression as partition key")));
18822 * Apply collation override if any
18824 if (pelem
->collation
)
18825 attcollation
= get_collation_oid(pelem
->collation
, false);
18828 * Check we have a collation iff it's a collatable type. The only
18829 * expected failures here are (1) COLLATE applied to a noncollatable
18830 * type, or (2) partition expression had an unresolved collation. But
18831 * we might as well code this to be a complete consistency check.
18833 if (type_is_collatable(atttype
))
18835 if (!OidIsValid(attcollation
))
18837 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
18838 errmsg("could not determine which collation to use for partition expression"),
18839 errhint("Use the COLLATE clause to set the collation explicitly.")));
18843 if (OidIsValid(attcollation
))
18845 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18846 errmsg("collations are not supported by type %s",
18847 format_type_be(atttype
))));
18850 partcollation
[attn
] = attcollation
;
18853 * Identify the appropriate operator class. For list and range
18854 * partitioning, we use a btree operator class; hash partitioning uses
18855 * a hash operator class.
18857 if (strategy
== PARTITION_STRATEGY_HASH
)
18858 am_oid
= HASH_AM_OID
;
18860 am_oid
= BTREE_AM_OID
;
18862 if (!pelem
->opclass
)
18864 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
18866 if (!OidIsValid(partopclass
[attn
]))
18868 if (strategy
== PARTITION_STRATEGY_HASH
)
18870 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18871 errmsg("data type %s has no default operator class for access method \"%s\"",
18872 format_type_be(atttype
), "hash"),
18873 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18876 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18877 errmsg("data type %s has no default operator class for access method \"%s\"",
18878 format_type_be(atttype
), "btree"),
18879 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18883 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
18885 am_oid
== HASH_AM_OID
? "hash" : "btree",
18893 * PartConstraintImpliedByRelConstraint
18894 * Do scanrel's existing constraints imply the partition constraint?
18896 * "Existing constraints" include its check constraints and column-level
18897 * not-null constraints. partConstraint describes the partition constraint,
18898 * in implicit-AND form.
18901 PartConstraintImpliedByRelConstraint(Relation scanrel
,
18902 List
*partConstraint
)
18904 List
*existConstraint
= NIL
;
18905 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18908 if (constr
&& constr
->has_not_null
)
18910 int natts
= scanrel
->rd_att
->natts
;
18912 for (i
= 1; i
<= natts
; i
++)
18914 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
18916 if (att
->attnotnull
&& !att
->attisdropped
)
18918 NullTest
*ntest
= makeNode(NullTest
);
18920 ntest
->arg
= (Expr
*) makeVar(1,
18926 ntest
->nulltesttype
= IS_NOT_NULL
;
18929 * argisrow=false is correct even for a composite column,
18930 * because attnotnull does not represent a SQL-spec IS NOT
18931 * NULL test in such a case, just IS DISTINCT FROM NULL.
18933 ntest
->argisrow
= false;
18934 ntest
->location
= -1;
18935 existConstraint
= lappend(existConstraint
, ntest
);
18940 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
18944 * ConstraintImpliedByRelConstraint
18945 * Do scanrel's existing constraints imply the given constraint?
18947 * testConstraint is the constraint to validate. provenConstraint is a
18948 * caller-provided list of conditions which this function may assume
18949 * to be true. Both provenConstraint and testConstraint must be in
18950 * implicit-AND form, must only contain immutable clauses, and must
18951 * contain only Vars with varno = 1.
18954 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
18956 List
*existConstraint
= list_copy(provenConstraint
);
18957 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18961 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
18962 for (i
= 0; i
< num_check
; i
++)
18967 * If this constraint hasn't been fully validated yet, we must ignore
18970 if (!constr
->check
[i
].ccvalid
)
18973 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
18976 * Run each expression through const-simplification and
18977 * canonicalization. It is necessary, because we will be comparing it
18978 * to similarly-processed partition constraint expressions, and may
18979 * fail to detect valid matches without this.
18981 cexpr
= eval_const_expressions(NULL
, cexpr
);
18982 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
18984 existConstraint
= list_concat(existConstraint
,
18985 make_ands_implicit((Expr
*) cexpr
));
18989 * Try to make the proof. Since we are comparing CHECK constraints, we
18990 * need to use weak implication, i.e., we assume existConstraint is
18991 * not-false and try to prove the same for testConstraint.
18993 * Note that predicate_implied_by assumes its first argument is known
18994 * immutable. That should always be true for both NOT NULL and partition
18995 * constraints, so we don't test it here.
18997 return predicate_implied_by(testConstraint
, existConstraint
, true);
19001 * QueuePartitionConstraintValidation
19003 * Add an entry to wqueue to have the given partition constraint validated by
19004 * Phase 3, for the given relation, and all its children.
19006 * We first verify whether the given constraint is implied by pre-existing
19007 * relation constraints; if it is, there's no need to scan the table to
19008 * validate, so don't queue in that case.
19011 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
19012 List
*partConstraint
,
19013 bool validate_default
)
19016 * Based on the table's existing constraints, determine whether or not we
19017 * may skip scanning the table.
19019 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
19021 if (!validate_default
)
19023 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19024 RelationGetRelationName(scanrel
))));
19027 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19028 RelationGetRelationName(scanrel
))));
19033 * Constraints proved insufficient. For plain relations, queue a
19034 * validation item now; for partitioned tables, recurse to process each
19037 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
19039 AlteredTableInfo
*tab
;
19041 /* Grab a work queue entry. */
19042 tab
= ATGetQueueEntry(wqueue
, scanrel
);
19043 Assert(tab
->partition_constraint
== NULL
);
19044 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
19045 tab
->validate_default
= validate_default
;
19047 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19049 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
19052 for (i
= 0; i
< partdesc
->nparts
; i
++)
19055 List
*thisPartConstraint
;
19058 * This is the minimum lock we need to prevent deadlocks.
19060 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
19063 * Adjust the constraint for scanrel so that it matches this
19064 * partition's attribute numbers.
19066 thisPartConstraint
=
19067 map_partition_varattnos(partConstraint
, 1,
19068 part_rel
, scanrel
);
19070 QueuePartitionConstraintValidation(wqueue
, part_rel
,
19071 thisPartConstraint
,
19073 table_close(part_rel
, NoLock
); /* keep lock till commit */
19079 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19081 * Return the address of the newly attached partition.
19083 static ObjectAddress
19084 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
19085 AlterTableUtilityContext
*context
)
19087 Relation attachrel
,
19089 List
*attachrel_children
;
19090 List
*partConstraint
;
19095 TupleDesc tupleDesc
;
19096 ObjectAddress address
;
19097 const char *trigger_name
;
19098 Oid defaultPartOid
;
19099 List
*partBoundConstraint
;
19100 ParseState
*pstate
= make_parsestate(NULL
);
19102 pstate
->p_sourcetext
= context
->queryString
;
19105 * We must lock the default partition if one exists, because attaching a
19106 * new partition will change its partition constraint.
19109 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19110 if (OidIsValid(defaultPartOid
))
19111 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19113 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
19116 * XXX I think it'd be a good idea to grab locks on all tables referenced
19117 * by FKs at this point also.
19121 * Must be owner of both parent and source table -- parent was checked by
19122 * ATSimplePermissions call in ATPrepCmd
19124 ATSimplePermissions(AT_AttachPartition
, attachrel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
19126 /* A partition can only have one parent */
19127 if (attachrel
->rd_rel
->relispartition
)
19129 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19130 errmsg("\"%s\" is already a partition",
19131 RelationGetRelationName(attachrel
))));
19133 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
19135 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19136 errmsg("cannot attach a typed table as partition")));
19139 * Table being attached should not already be part of inheritance; either
19140 * as a child table...
19142 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
19144 Anum_pg_inherits_inhrelid
,
19145 BTEqualStrategyNumber
, F_OIDEQ
,
19146 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19147 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
19149 if (HeapTupleIsValid(systable_getnext(scan
)))
19151 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19152 errmsg("cannot attach inheritance child as partition")));
19153 systable_endscan(scan
);
19155 /* ...or as a parent table (except the case when it is partitioned) */
19157 Anum_pg_inherits_inhparent
,
19158 BTEqualStrategyNumber
, F_OIDEQ
,
19159 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
19160 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
19162 if (HeapTupleIsValid(systable_getnext(scan
)) &&
19163 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
19165 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19166 errmsg("cannot attach inheritance parent as partition")));
19167 systable_endscan(scan
);
19168 table_close(catalog
, AccessShareLock
);
19171 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19172 * particular, this disallows making a rel a partition of itself.)
19174 * We do that by checking if rel is a member of the list of attachrel's
19175 * partitions provided the latter is partitioned at all. We want to avoid
19176 * having to construct this list again, so we request the strongest lock
19177 * on all partitions. We need the strongest lock, because we may decide
19178 * to scan them if we find out that the table being attached (or its leaf
19179 * partitions) may contain rows that violate the partition constraint. If
19180 * the table has a constraint that would prevent such rows, which by
19181 * definition is present in all the partitions, we need not scan the
19182 * table, nor its partitions. But we cannot risk a deadlock by taking a
19183 * weaker lock now and the stronger one only when needed.
19185 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
19186 AccessExclusiveLock
, NULL
);
19187 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
19189 (errcode(ERRCODE_DUPLICATE_TABLE
),
19190 errmsg("circular inheritance not allowed"),
19191 errdetail("\"%s\" is already a child of \"%s\".",
19192 RelationGetRelationName(rel
),
19193 RelationGetRelationName(attachrel
))));
19195 /* If the parent is permanent, so must be all of its partitions. */
19196 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
19197 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
19199 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19200 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19201 RelationGetRelationName(rel
))));
19203 /* Temp parent cannot have a partition that is itself not a temp */
19204 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19205 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
19207 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19208 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19209 RelationGetRelationName(rel
))));
19211 /* If the parent is temp, it must belong to this session */
19212 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19213 !rel
->rd_islocaltemp
)
19215 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19216 errmsg("cannot attach as partition of temporary relation of another session")));
19218 /* Ditto for the partition */
19219 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
19220 !attachrel
->rd_islocaltemp
)
19222 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19223 errmsg("cannot attach temporary relation of another session as partition")));
19226 * Check if attachrel has any identity columns or any columns that aren't
19229 tupleDesc
= RelationGetDescr(attachrel
);
19230 natts
= tupleDesc
->natts
;
19231 for (attno
= 1; attno
<= natts
; attno
++)
19233 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
19234 char *attributeName
= NameStr(attribute
->attname
);
19236 /* Ignore dropped */
19237 if (attribute
->attisdropped
)
19240 if (attribute
->attidentity
)
19242 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19243 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19244 RelationGetRelationName(attachrel
), attributeName
),
19245 errdetail("The new partition may not contain an identity column."));
19247 /* Try to find the column in parent (matching on column name) */
19248 if (!SearchSysCacheExists2(ATTNAME
,
19249 ObjectIdGetDatum(RelationGetRelid(rel
)),
19250 CStringGetDatum(attributeName
)))
19252 (errcode(ERRCODE_DATATYPE_MISMATCH
),
19253 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19254 RelationGetRelationName(attachrel
), attributeName
,
19255 RelationGetRelationName(rel
)),
19256 errdetail("The new partition may contain only the columns present in parent.")));
19260 * If child_rel has row-level triggers with transition tables, we
19261 * currently don't allow it to become a partition. See also prohibitions
19262 * in ATExecAddInherit() and CreateTrigger().
19264 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
19265 if (trigger_name
!= NULL
)
19267 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
19268 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19269 trigger_name
, RelationGetRelationName(attachrel
)),
19270 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19273 * Check that the new partition's bound is valid and does not overlap any
19274 * of existing partitions of the parent - note that it does not return on
19277 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
19278 cmd
->bound
, pstate
);
19280 /* OK to create inheritance. Rest of the checks performed there */
19281 CreateInheritance(attachrel
, rel
, true);
19283 /* Update the pg_class entry. */
19284 StorePartitionBound(attachrel
, rel
, cmd
->bound
);
19286 /* Ensure there exists a correct set of indexes in the partition. */
19287 AttachPartitionEnsureIndexes(wqueue
, rel
, attachrel
);
19290 CloneRowTriggersToPartition(rel
, attachrel
);
19293 * Clone foreign key constraints. Callee is responsible for setting up
19294 * for phase 3 constraint verification.
19296 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
19299 * Generate partition constraint from the partition bound specification.
19300 * If the parent itself is a partition, make sure to include its
19301 * constraint as well.
19303 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
19304 partConstraint
= list_concat(partBoundConstraint
,
19305 RelationGetPartitionQual(rel
));
19307 /* Skip validation if there are no constraints to validate. */
19308 if (partConstraint
)
19311 * Run the partition quals through const-simplification similar to
19312 * check constraints. We skip canonicalize_qual, though, because
19313 * partition quals should be in canonical form already.
19316 (List
*) eval_const_expressions(NULL
,
19317 (Node
*) partConstraint
);
19319 /* XXX this sure looks wrong */
19320 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
19323 * Adjust the generated constraint to match this partition's attribute
19326 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
19329 /* Validate partition constraints against the table being attached. */
19330 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
19335 * If we're attaching a partition other than the default partition and a
19336 * default one exists, then that partition's partition constraint changes,
19337 * so add an entry to the work queue to validate it, too. (We must not do
19338 * this when the partition being attached is the default one; we already
19341 if (OidIsValid(defaultPartOid
))
19343 Relation defaultrel
;
19344 List
*defPartConstraint
;
19346 Assert(!cmd
->bound
->is_default
);
19348 /* we already hold a lock on the default partition */
19349 defaultrel
= table_open(defaultPartOid
, NoLock
);
19350 defPartConstraint
=
19351 get_proposed_default_constraint(partBoundConstraint
);
19354 * Map the Vars in the constraint expression from rel's attnos to
19357 defPartConstraint
=
19358 map_partition_varattnos(defPartConstraint
,
19359 1, defaultrel
, rel
);
19360 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
19361 defPartConstraint
, true);
19363 /* keep our lock until commit. */
19364 table_close(defaultrel
, NoLock
);
19367 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
19370 * If the partition we just attached is partitioned itself, invalidate
19371 * relcache for all descendent partitions too to ensure that their
19372 * rd_partcheck expression trees are rebuilt; partitions already locked at
19373 * the beginning of this function.
19375 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19379 foreach(l
, attachrel_children
)
19381 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
19385 /* keep our lock until commit */
19386 table_close(attachrel
, NoLock
);
19392 * AttachPartitionEnsureIndexes
19393 * subroutine for ATExecAttachPartition to create/match indexes
19395 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19396 * PARTITION: every partition must have an index attached to each index on the
19397 * partitioned table.
19400 AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
)
19403 List
*attachRelIdxs
;
19404 Relation
*attachrelIdxRels
;
19405 IndexInfo
**attachInfos
;
19408 MemoryContext oldcxt
;
19410 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
19411 "AttachPartitionEnsureIndexes",
19412 ALLOCSET_DEFAULT_SIZES
);
19413 oldcxt
= MemoryContextSwitchTo(cxt
);
19415 idxes
= RelationGetIndexList(rel
);
19416 attachRelIdxs
= RelationGetIndexList(attachrel
);
19417 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
19418 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
19420 /* Build arrays of all existing indexes and their IndexInfos */
19421 foreach(cell
, attachRelIdxs
)
19423 Oid cldIdxId
= lfirst_oid(cell
);
19424 int i
= foreach_current_index(cell
);
19426 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
19427 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
19431 * If we're attaching a foreign table, we must fail if any of the indexes
19432 * is a constraint index; otherwise, there's nothing to do here. Do this
19433 * before starting work, to avoid wasting the effort of building a few
19434 * non-unique indexes before coming across a unique one.
19436 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
19438 foreach(cell
, idxes
)
19440 Oid idx
= lfirst_oid(cell
);
19441 Relation idxRel
= index_open(idx
, AccessShareLock
);
19443 if (idxRel
->rd_index
->indisunique
||
19444 idxRel
->rd_index
->indisprimary
)
19446 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
19447 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19448 RelationGetRelationName(attachrel
),
19449 RelationGetRelationName(rel
)),
19450 errdetail("Partitioned table \"%s\" contains unique indexes.",
19451 RelationGetRelationName(rel
))));
19452 index_close(idxRel
, AccessShareLock
);
19459 * For each index on the partitioned table, find a matching one in the
19460 * partition-to-be; if one is not found, create one.
19462 foreach(cell
, idxes
)
19464 Oid idx
= lfirst_oid(cell
);
19465 Relation idxRel
= index_open(idx
, AccessShareLock
);
19468 bool found
= false;
19472 * Ignore indexes in the partitioned table other than partitioned
19475 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
19477 index_close(idxRel
, AccessShareLock
);
19481 /* construct an indexinfo to compare existing indexes against */
19482 info
= BuildIndexInfo(idxRel
);
19483 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
19484 RelationGetDescr(rel
),
19486 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
19489 * Scan the list of existing indexes in the partition-to-be, and mark
19490 * the first matching, valid, unattached one we find, if any, as
19491 * partition of the parent index. If we find one, we're done.
19493 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19495 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
19496 Oid cldConstrOid
= InvalidOid
;
19498 /* does this index have a parent? if so, can't use it */
19499 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
19502 /* If this index is invalid, can't use it */
19503 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
19506 if (CompareIndexInfo(attachInfos
[i
], info
,
19507 attachrelIdxRels
[i
]->rd_indcollation
,
19508 idxRel
->rd_indcollation
,
19509 attachrelIdxRels
[i
]->rd_opfamily
,
19510 idxRel
->rd_opfamily
,
19514 * If this index is being created in the parent because of a
19515 * constraint, then the child needs to have a constraint also,
19516 * so look for one. If there is no such constraint, this
19517 * index is no good, so keep looking.
19519 if (OidIsValid(constraintOid
))
19522 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
19525 if (!OidIsValid(cldConstrOid
))
19530 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
19531 if (OidIsValid(constraintOid
))
19532 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
19533 RelationGetRelid(attachrel
));
19536 CommandCounterIncrement();
19542 * If no suitable index was found in the partition-to-be, create one
19550 stmt
= generateClonedIndexStmt(NULL
,
19555 * If the index is a primary key, mark all columns as NOT NULL if
19556 * they aren't already.
19560 MemoryContextSwitchTo(oldcxt
);
19561 for (int j
= 0; j
< info
->ii_NumIndexKeyAttrs
; j
++)
19563 AttrNumber childattno
;
19565 childattno
= get_attnum(RelationGetRelid(attachrel
),
19566 get_attname(RelationGetRelid(rel
),
19567 info
->ii_IndexAttrNumbers
[j
],
19569 set_attnotnull(wqueue
, attachrel
, childattno
,
19570 true, AccessExclusiveLock
);
19572 MemoryContextSwitchTo(cxt
);
19575 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
19576 RelationGetRelid(idxRel
),
19579 true, false, false, false, false);
19582 index_close(idxRel
, AccessShareLock
);
19587 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
19588 index_close(attachrelIdxRels
[i
], AccessShareLock
);
19589 MemoryContextSwitchTo(oldcxt
);
19590 MemoryContextDelete(cxt
);
19594 * CloneRowTriggersToPartition
19595 * subroutine for ATExecAttachPartition/DefineRelation to create row
19596 * triggers on partitions
19599 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
19601 Relation pg_trigger
;
19605 MemoryContext perTupCxt
;
19607 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19608 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
19609 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
19610 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
19611 true, NULL
, 1, &key
);
19613 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
19614 "clone trig", ALLOCSET_SMALL_SIZES
);
19616 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
19618 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
19619 CreateTrigStmt
*trigStmt
;
19624 List
*trigargs
= NIL
;
19625 MemoryContext oldcxt
;
19628 * Ignore statement-level triggers; those are not cloned.
19630 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
19634 * Don't clone internal triggers, because the constraint cloning code
19637 if (trigForm
->tgisinternal
)
19641 * Complain if we find an unexpected trigger type.
19643 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
19644 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
19645 elog(ERROR
, "unexpected trigger \"%s\" found",
19646 NameStr(trigForm
->tgname
));
19648 /* Use short-lived context for CREATE TRIGGER */
19649 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
19652 * If there is a WHEN clause, generate a 'cooked' version of it that's
19653 * appropriate for the partition.
19655 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
19656 RelationGetDescr(pg_trigger
), &isnull
);
19659 qual
= stringToNode(TextDatumGetCString(value
));
19660 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
19661 partition
, parent
);
19662 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
19663 partition
, parent
);
19667 * If there is a column list, transform it to a list of column names.
19668 * Note we don't need to map this list in any way ...
19670 if (trigForm
->tgattr
.dim1
> 0)
19674 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
19676 Form_pg_attribute col
;
19678 col
= TupleDescAttr(parent
->rd_att
,
19679 trigForm
->tgattr
.values
[i
] - 1);
19680 cols
= lappend(cols
,
19681 makeString(pstrdup(NameStr(col
->attname
))));
19685 /* Reconstruct trigger arguments list. */
19686 if (trigForm
->tgnargs
> 0)
19690 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
19691 RelationGetDescr(pg_trigger
), &isnull
);
19693 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19694 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
19696 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
19698 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
19700 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
19701 p
+= strlen(p
) + 1;
19705 trigStmt
= makeNode(CreateTrigStmt
);
19706 trigStmt
->replace
= false;
19707 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
19708 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
19709 trigStmt
->relation
= NULL
;
19710 trigStmt
->funcname
= NULL
; /* passed separately */
19711 trigStmt
->args
= trigargs
;
19712 trigStmt
->row
= true;
19713 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
19714 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
19715 trigStmt
->columns
= cols
;
19716 trigStmt
->whenClause
= NULL
; /* passed separately */
19717 trigStmt
->transitionRels
= NIL
; /* not supported at present */
19718 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
19719 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
19720 trigStmt
->constrrel
= NULL
; /* passed separately */
19722 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
19723 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
19724 trigForm
->tgfoid
, trigForm
->oid
, qual
,
19725 false, true, trigForm
->tgenabled
);
19727 MemoryContextSwitchTo(oldcxt
);
19728 MemoryContextReset(perTupCxt
);
19731 MemoryContextDelete(perTupCxt
);
19733 systable_endscan(scan
);
19734 table_close(pg_trigger
, RowExclusiveLock
);
19738 * ALTER TABLE DETACH PARTITION
19740 * Return the address of the relation that is no longer a partition of rel.
19742 * If concurrent mode is requested, we run in two transactions. A side-
19743 * effect is that this command cannot run in a multi-part ALTER TABLE.
19744 * Currently, that's enforced by the grammar.
19746 * The strategy for concurrency is to first modify the partition's
19747 * pg_inherit catalog row to make it visible to everyone that the
19748 * partition is detached, lock the partition against writes, and commit
19749 * the transaction; anyone who requests the partition descriptor from
19750 * that point onwards has to ignore such a partition. In a second
19751 * transaction, we wait until all transactions that could have seen the
19752 * partition as attached are gone, then we remove the rest of partition
19753 * metadata (pg_inherits and pg_class.relpartbounds).
19755 static ObjectAddress
19756 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
19757 RangeVar
*name
, bool concurrent
)
19760 ObjectAddress address
;
19761 Oid defaultPartOid
;
19764 * We must lock the default partition, because detaching this partition
19765 * will change its partition constraint.
19768 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
19769 if (OidIsValid(defaultPartOid
))
19772 * Concurrent detaching when a default partition exists is not
19773 * supported. The main problem is that the default partition
19774 * constraint would change. And there's a definitional problem: what
19775 * should happen to the tuples that are being inserted that belong to
19776 * the partition being detached? Putting them on the partition being
19777 * detached would be wrong, since they'd become "lost" after the
19778 * detaching completes but we cannot put them in the default partition
19779 * either until we alter its partition constraint.
19781 * I think we could solve this problem if we effected the constraint
19782 * change before committing the first transaction. But the lock would
19783 * have to remain AEL and it would cause concurrent query planning to
19784 * be blocked, so changing it that way would be even worse.
19788 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19789 errmsg("cannot detach partitions concurrently when a default partition exists")));
19790 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19794 * In concurrent mode, the partition is locked with share-update-exclusive
19795 * in the first transaction. This allows concurrent transactions to be
19796 * doing DML to the partition.
19798 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
19799 AccessExclusiveLock
);
19802 * Check inheritance conditions and either delete the pg_inherits row (in
19803 * non-concurrent mode) or just set the inhdetachpending flag.
19806 RemoveInheritance(partRel
, rel
, false);
19808 MarkInheritDetached(partRel
, rel
);
19811 * Ensure that foreign keys still hold after this detach. This keeps
19812 * locks on the referencing tables, which prevents concurrent transactions
19813 * from adding rows that we wouldn't see. For this to work in concurrent
19814 * mode, it is critical that the partition appears as no longer attached
19815 * for the RI queries as soon as the first transaction commits.
19817 ATDetachCheckNoForeignKeyRefs(partRel
);
19820 * Concurrent mode has to work harder; first we add a new constraint to
19821 * the partition that matches the partition constraint. Then we close our
19822 * existing transaction, and in a new one wait for all processes to catch
19823 * up on the catalog updates we've done so far; at that point we can
19824 * complete the operation.
19831 char *parentrelname
;
19835 * Add a new constraint to the partition being detached, which
19836 * supplants the partition constraint (unless there is one already).
19838 DetachAddConstraintIfNeeded(wqueue
, partRel
);
19841 * We're almost done now; the only traces that remain are the
19842 * pg_inherits tuple and the partition's relpartbounds. Before we can
19843 * remove those, we need to wait until all transactions that know that
19844 * this is a partition are gone.
19848 * Remember relation OIDs to re-acquire them later; and relation names
19849 * too, for error messages if something is dropped in between.
19851 partrelid
= RelationGetRelid(partRel
);
19852 parentrelid
= RelationGetRelid(rel
);
19853 parentrelname
= MemoryContextStrdup(PortalContext
,
19854 RelationGetRelationName(rel
));
19855 partrelname
= MemoryContextStrdup(PortalContext
,
19856 RelationGetRelationName(partRel
));
19858 /* Invalidate relcache entries for the parent -- must be before close */
19859 CacheInvalidateRelcache(rel
);
19861 table_close(partRel
, NoLock
);
19862 table_close(rel
, NoLock
);
19865 /* Make updated catalog entry visible */
19866 PopActiveSnapshot();
19867 CommitTransactionCommand();
19869 StartTransactionCommand();
19872 * Now wait. This ensures that all queries that were planned
19873 * including the partition are finished before we remove the rest of
19874 * catalog entries. We don't need or indeed want to acquire this
19875 * lock, though -- that would block later queries.
19877 * We don't need to concern ourselves with waiting for a lock on the
19878 * partition itself, since we will acquire AccessExclusiveLock below.
19880 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
19881 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
19884 * Now acquire locks in both relations again. Note they may have been
19885 * removed in the meantime, so care is required.
19887 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
19888 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
19890 /* If the relations aren't there, something bad happened; bail out */
19893 if (partRel
!= NULL
) /* shouldn't happen */
19894 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
19897 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19898 errmsg("partitioned table \"%s\" was removed concurrently",
19901 if (partRel
== NULL
)
19903 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19904 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
19909 /* Do the final part of detaching */
19910 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
19912 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19914 /* keep our lock until commit */
19915 table_close(partRel
, NoLock
);
19921 * Second part of ALTER TABLE .. DETACH.
19923 * This is separate so that it can be run independently when the second
19924 * transaction of the concurrent algorithm fails (crash or abort).
19927 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
19928 Oid defaultPartOid
)
19934 Datum new_val
[Natts_pg_class
];
19935 bool new_null
[Natts_pg_class
],
19936 new_repl
[Natts_pg_class
];
19939 Relation trigrel
= NULL
;
19944 * We can remove the pg_inherits row now. (In the non-concurrent case,
19945 * this was already done).
19947 RemoveInheritance(partRel
, rel
, true);
19950 /* Drop any triggers that were cloned on creation/attach. */
19951 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
19954 * Detach any foreign keys that are inherited. This includes creating
19955 * additional action triggers.
19957 fks
= copyObject(RelationGetFKeyList(partRel
));
19959 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19962 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
19964 Form_pg_constraint conform
;
19965 Constraint
*fkconstraint
;
19966 Oid insertTriggerOid
,
19969 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
19970 if (!HeapTupleIsValid(contup
))
19971 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
19972 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
19974 /* consider only the inherited foreign keys */
19975 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
19976 !OidIsValid(conform
->conparentid
))
19978 ReleaseSysCache(contup
);
19982 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19983 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
19986 * Also, look up the partition's "check" triggers corresponding to the
19987 * constraint being detached and detach them from the parent triggers.
19989 GetForeignKeyCheckTriggers(trigrel
,
19990 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
19991 &insertTriggerOid
, &updateTriggerOid
);
19992 Assert(OidIsValid(insertTriggerOid
));
19993 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
19994 RelationGetRelid(partRel
));
19995 Assert(OidIsValid(updateTriggerOid
));
19996 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
19997 RelationGetRelid(partRel
));
20000 * Make the action triggers on the referenced relation. When this was
20001 * a partition the action triggers pointed to the parent rel (they
20002 * still do), but now we need separate ones of our own.
20004 fkconstraint
= makeNode(Constraint
);
20005 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
20006 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
20007 fkconstraint
->deferrable
= conform
->condeferrable
;
20008 fkconstraint
->initdeferred
= conform
->condeferred
;
20009 fkconstraint
->location
= -1;
20010 fkconstraint
->pktable
= NULL
;
20011 fkconstraint
->fk_attrs
= NIL
;
20012 fkconstraint
->pk_attrs
= NIL
;
20013 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
20014 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
20015 fkconstraint
->fk_del_action
= conform
->confdeltype
;
20016 fkconstraint
->fk_del_set_cols
= NIL
;
20017 fkconstraint
->old_conpfeqop
= NIL
;
20018 fkconstraint
->old_pktable_oid
= InvalidOid
;
20019 fkconstraint
->skip_validation
= false;
20020 fkconstraint
->initially_valid
= true;
20022 createForeignKeyActionTriggers(partRel
, conform
->confrelid
,
20023 fkconstraint
, fk
->conoid
,
20025 InvalidOid
, InvalidOid
,
20028 ReleaseSysCache(contup
);
20030 list_free_deep(fks
);
20032 table_close(trigrel
, RowExclusiveLock
);
20035 * Any sub-constraints that are in the referenced-side of a larger
20036 * constraint have to be removed. This partition is no longer part of the
20037 * key space of the constraint.
20039 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
20041 Oid constrOid
= lfirst_oid(cell
);
20042 ObjectAddress constraint
;
20044 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20045 deleteDependencyRecordsForClass(ConstraintRelationId
,
20047 ConstraintRelationId
,
20048 DEPENDENCY_INTERNAL
);
20049 CommandCounterIncrement();
20051 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
20052 performDeletion(&constraint
, DROP_RESTRICT
, 0);
20055 /* Now we can detach indexes */
20056 indexes
= RelationGetIndexList(partRel
);
20057 foreach(cell
, indexes
)
20059 Oid idxid
= lfirst_oid(cell
);
20063 if (!has_superclass(idxid
))
20066 Assert((IndexGetRelation(get_partition_parent(idxid
, false), false) ==
20067 RelationGetRelid(rel
)));
20069 idx
= index_open(idxid
, AccessExclusiveLock
);
20070 IndexSetParentIndex(idx
, InvalidOid
);
20072 /* If there's a constraint associated with the index, detach it too */
20073 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
20075 if (OidIsValid(constrOid
))
20076 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
20078 index_close(idx
, NoLock
);
20081 /* Update pg_class tuple */
20082 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
20083 tuple
= SearchSysCacheCopy1(RELOID
,
20084 ObjectIdGetDatum(RelationGetRelid(partRel
)));
20085 if (!HeapTupleIsValid(tuple
))
20086 elog(ERROR
, "cache lookup failed for relation %u",
20087 RelationGetRelid(partRel
));
20088 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
20090 /* Clear relpartbound and reset relispartition */
20091 memset(new_val
, 0, sizeof(new_val
));
20092 memset(new_null
, false, sizeof(new_null
));
20093 memset(new_repl
, false, sizeof(new_repl
));
20094 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
20095 new_null
[Anum_pg_class_relpartbound
- 1] = true;
20096 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
20097 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
20098 new_val
, new_null
, new_repl
);
20100 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
20101 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
20102 heap_freetuple(newtuple
);
20103 table_close(classRel
, RowExclusiveLock
);
20106 * Drop identity property from all identity columns of partition.
20108 for (int attno
= 0; attno
< RelationGetNumberOfAttributes(partRel
); attno
++)
20110 Form_pg_attribute attr
= TupleDescAttr(partRel
->rd_att
, attno
);
20112 if (!attr
->attisdropped
&& attr
->attidentity
)
20113 ATExecDropIdentity(partRel
, NameStr(attr
->attname
), false,
20114 AccessExclusiveLock
, true, true);
20117 if (OidIsValid(defaultPartOid
))
20120 * If the relation being detached is the default partition itself,
20121 * remove it from the parent's pg_partitioned_table entry.
20123 * If not, we must invalidate default partition's relcache entry, as
20124 * in StorePartitionBound: its partition constraint depends on every
20125 * other partition's partition constraint.
20127 if (RelationGetRelid(partRel
) == defaultPartOid
)
20128 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
20130 CacheInvalidateRelcacheByRelid(defaultPartOid
);
20134 * Invalidate the parent's relcache so that the partition is no longer
20135 * included in its partition descriptor.
20137 CacheInvalidateRelcache(rel
);
20140 * If the partition we just detached is partitioned itself, invalidate
20141 * relcache for all descendent partitions too to ensure that their
20142 * rd_partcheck expression trees are rebuilt; must lock partitions before
20143 * doing so, using the same lockmode as what partRel has been locked with
20146 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
20150 children
= find_all_inheritors(RelationGetRelid(partRel
),
20151 AccessExclusiveLock
, NULL
);
20152 foreach(cell
, children
)
20154 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
20160 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20162 * To use when a DETACH PARTITION command previously did not run to
20163 * completion; this completes the detaching process.
20165 static ObjectAddress
20166 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
20169 ObjectAddress address
;
20170 Snapshot snap
= GetActiveSnapshot();
20172 partRel
= table_openrv(name
, AccessExclusiveLock
);
20175 * Wait until existing snapshots are gone. This is important if the
20176 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20177 * user could immediately run DETACH FINALIZE without actually waiting for
20178 * existing transactions. We must not complete the detach action until
20179 * all such queries are complete (otherwise we would present them with an
20180 * inconsistent view of catalogs).
20182 WaitForOlderSnapshots(snap
->xmin
, false);
20184 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
20186 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
20188 table_close(partRel
, NoLock
);
20194 * DetachAddConstraintIfNeeded
20195 * Subroutine for ATExecDetachPartition. Create a constraint that
20196 * takes the place of the partition constraint, but avoid creating
20197 * a dupe if an constraint already exists which implies the needed
20201 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
20203 List
*constraintExpr
;
20205 constraintExpr
= RelationGetPartitionQual(partRel
);
20206 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
20209 * Avoid adding a new constraint if the needed constraint is implied by an
20210 * existing constraint
20212 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
20214 AlteredTableInfo
*tab
;
20217 tab
= ATGetQueueEntry(wqueue
, partRel
);
20219 /* Add constraint on partition, equivalent to the partition constraint */
20220 n
= makeNode(Constraint
);
20221 n
->contype
= CONSTR_CHECK
;
20224 n
->is_no_inherit
= false;
20225 n
->raw_expr
= NULL
;
20226 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
20227 n
->initially_valid
= true;
20228 n
->skip_validation
= true;
20229 /* It's a re-add, since it nominally already exists */
20230 ATAddCheckNNConstraint(wqueue
, tab
, partRel
, n
,
20231 true, false, true, ShareUpdateExclusiveLock
);
20236 * DropClonedTriggersFromPartition
20237 * subroutine for ATExecDetachPartition to remove any triggers that were
20238 * cloned to the partition when it was created-as-partition or attached.
20239 * This undoes what CloneRowTriggersToPartition did.
20242 DropClonedTriggersFromPartition(Oid partitionId
)
20248 ObjectAddresses
*objects
;
20250 objects
= new_object_addresses();
20253 * Scan pg_trigger to search for all triggers on this rel.
20255 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
20256 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
20257 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
20258 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
20259 true, NULL
, 1, &skey
);
20260 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
20262 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
20263 ObjectAddress trig
;
20265 /* Ignore triggers that weren't cloned */
20266 if (!OidIsValid(pg_trigger
->tgparentid
))
20270 * Ignore internal triggers that are implementation objects of foreign
20271 * keys, because these will be detached when the foreign keys
20274 if (OidIsValid(pg_trigger
->tgconstrrelid
))
20278 * This is ugly, but necessary: remove the dependency markings on the
20279 * trigger so that it can be removed.
20281 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20283 DEPENDENCY_PARTITION_PRI
);
20284 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
20285 RelationRelationId
,
20286 DEPENDENCY_PARTITION_SEC
);
20288 /* remember this trigger to remove it below */
20289 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
20290 add_exact_object_address(&trig
, objects
);
20293 /* make the dependency removal visible to the deletion below */
20294 CommandCounterIncrement();
20295 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
20298 free_object_addresses(objects
);
20299 systable_endscan(scan
);
20300 table_close(tgrel
, RowExclusiveLock
);
20304 * Before acquiring lock on an index, acquire the same lock on the owning
20307 struct AttachIndexCallbackState
20311 bool lockedParentTbl
;
20315 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
20318 struct AttachIndexCallbackState
*state
;
20319 Form_pg_class classform
;
20322 state
= (struct AttachIndexCallbackState
*) arg
;
20324 if (!state
->lockedParentTbl
)
20326 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
20327 state
->lockedParentTbl
= true;
20331 * If we previously locked some other heap, and the name we're looking up
20332 * no longer refers to an index on that relation, release the now-useless
20333 * lock. XXX maybe we should do *after* we verify whether the index does
20334 * not actually belong to the same relation ...
20336 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
20338 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
20339 state
->partitionOid
= InvalidOid
;
20342 /* Didn't find a relation, so no need for locking or permission checks. */
20343 if (!OidIsValid(relOid
))
20346 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
20347 if (!HeapTupleIsValid(tuple
))
20348 return; /* concurrently dropped, so nothing to do */
20349 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
20350 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
20351 classform
->relkind
!= RELKIND_INDEX
)
20353 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20354 errmsg("\"%s\" is not an index", rv
->relname
)));
20355 ReleaseSysCache(tuple
);
20358 * Since we need only examine the heap's tupledesc, an access share lock
20359 * on it (preventing any DDL) is sufficient.
20361 state
->partitionOid
= IndexGetRelation(relOid
, false);
20362 LockRelationOid(state
->partitionOid
, AccessShareLock
);
20366 * ALTER INDEX i1 ATTACH PARTITION i2
20368 static ObjectAddress
20369 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
20373 Relation parentTbl
;
20374 ObjectAddress address
;
20377 struct AttachIndexCallbackState state
;
20380 * We need to obtain lock on the index 'name' to modify it, but we also
20381 * need to read its owning table's tuple descriptor -- so we need to lock
20382 * both. To avoid deadlocks, obtain lock on the table before doing so on
20383 * the index. Furthermore, we need to examine the parent table of the
20384 * partition, so lock that one too.
20386 state
.partitionOid
= InvalidOid
;
20387 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
20388 state
.lockedParentTbl
= false;
20390 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
20391 RangeVarCallbackForAttachIndex
,
20394 if (!OidIsValid(partIdxId
))
20396 (errcode(ERRCODE_UNDEFINED_OBJECT
),
20397 errmsg("index \"%s\" does not exist", name
->relname
)));
20399 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20400 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
20402 /* we already hold locks on both tables, so this is safe: */
20403 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
20404 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
20406 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
20408 /* Silently do nothing if already in the right state */
20409 currParent
= partIdx
->rd_rel
->relispartition
?
20410 get_partition_parent(partIdxId
, false) : InvalidOid
;
20411 if (currParent
!= RelationGetRelid(parentIdx
))
20413 IndexInfo
*childInfo
;
20414 IndexInfo
*parentInfo
;
20418 PartitionDesc partDesc
;
20420 cldConstrId
= InvalidOid
;
20423 * If this partition already has an index attached, refuse the
20426 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
20428 if (OidIsValid(currParent
))
20430 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20431 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20432 RelationGetRelationName(partIdx
),
20433 RelationGetRelationName(parentIdx
)),
20434 errdetail("Index \"%s\" is already attached to another index.",
20435 RelationGetRelationName(partIdx
))));
20437 /* Make sure it indexes a partition of the other index's table */
20438 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
20440 for (i
= 0; i
< partDesc
->nparts
; i
++)
20442 if (partDesc
->oids
[i
] == state
.partitionOid
)
20450 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20451 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20452 RelationGetRelationName(partIdx
),
20453 RelationGetRelationName(parentIdx
)),
20454 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20455 RelationGetRelationName(partIdx
),
20456 RelationGetRelationName(parentTbl
))));
20458 /* Ensure the indexes are compatible */
20459 childInfo
= BuildIndexInfo(partIdx
);
20460 parentInfo
= BuildIndexInfo(parentIdx
);
20461 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
20462 RelationGetDescr(parentTbl
),
20464 if (!CompareIndexInfo(childInfo
, parentInfo
,
20465 partIdx
->rd_indcollation
,
20466 parentIdx
->rd_indcollation
,
20467 partIdx
->rd_opfamily
,
20468 parentIdx
->rd_opfamily
,
20471 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20472 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20473 RelationGetRelationName(partIdx
),
20474 RelationGetRelationName(parentIdx
)),
20475 errdetail("The index definitions do not match.")));
20478 * If there is a constraint in the parent, make sure there is one in
20481 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
20482 RelationGetRelid(parentIdx
));
20484 if (OidIsValid(constraintOid
))
20486 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
20488 if (!OidIsValid(cldConstrId
))
20490 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
20491 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20492 RelationGetRelationName(partIdx
),
20493 RelationGetRelationName(parentIdx
)),
20494 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20495 RelationGetRelationName(parentIdx
),
20496 RelationGetRelationName(parentTbl
),
20497 RelationGetRelationName(partIdx
))));
20501 * If it's a primary key, make sure the columns in the partition are
20504 if (parentIdx
->rd_index
->indisprimary
)
20505 verifyPartitionIndexNotNull(childInfo
, partTbl
);
20507 /* All good -- do it */
20508 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
20509 if (OidIsValid(constraintOid
))
20510 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
20511 RelationGetRelid(partTbl
));
20513 free_attrmap(attmap
);
20515 validatePartitionedIndex(parentIdx
, parentTbl
);
20518 relation_close(parentTbl
, AccessShareLock
);
20519 /* keep these locks till commit */
20520 relation_close(partTbl
, NoLock
);
20521 relation_close(partIdx
, NoLock
);
20527 * Verify whether the given partition already contains an index attached
20528 * to the given partitioned index. If so, raise an error.
20531 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
20535 existingIdx
= index_get_partition(partitionTbl
,
20536 RelationGetRelid(parentIdx
));
20537 if (OidIsValid(existingIdx
))
20539 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
20540 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20541 RelationGetRelationName(partIdx
),
20542 RelationGetRelationName(parentIdx
)),
20543 errdetail("Another index is already attached for partition \"%s\".",
20544 RelationGetRelationName(partitionTbl
))));
20548 * Verify whether the set of attached partition indexes to a parent index on
20549 * a partitioned table is complete. If it is, mark the parent index valid.
20551 * This should be called each time a partition index is attached.
20554 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
20556 Relation inheritsRel
;
20561 bool updated
= false;
20563 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
20566 * Scan pg_inherits for this parent index. Count each valid index we find
20567 * (verifying the pg_index entry for each), and if we reach the total
20568 * amount we expect, we can mark this parent index as valid.
20570 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
20571 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
20572 BTEqualStrategyNumber
, F_OIDEQ
,
20573 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20574 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
20576 while ((inhTup
= systable_getnext(scan
)) != NULL
)
20578 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
20580 Form_pg_index indexForm
;
20582 indTup
= SearchSysCache1(INDEXRELID
,
20583 ObjectIdGetDatum(inhForm
->inhrelid
));
20584 if (!HeapTupleIsValid(indTup
))
20585 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
20586 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20587 if (indexForm
->indisvalid
)
20589 ReleaseSysCache(indTup
);
20592 /* Done with pg_inherits */
20593 systable_endscan(scan
);
20594 table_close(inheritsRel
, AccessShareLock
);
20597 * If we found as many inherited indexes as the partitioned table has
20598 * partitions, we're good; update pg_index to set indisvalid.
20600 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
20604 Form_pg_index indexForm
;
20606 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
20607 indTup
= SearchSysCacheCopy1(INDEXRELID
,
20608 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
20609 if (!HeapTupleIsValid(indTup
))
20610 elog(ERROR
, "cache lookup failed for index %u",
20611 RelationGetRelid(partedIdx
));
20612 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
20614 indexForm
->indisvalid
= true;
20617 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
20619 table_close(idxRel
, RowExclusiveLock
);
20620 heap_freetuple(indTup
);
20624 * If this index is in turn a partition of a larger index, validating it
20625 * might cause the parent to become valid also. Try that.
20627 if (updated
&& partedIdx
->rd_rel
->relispartition
)
20631 Relation parentIdx
,
20634 /* make sure we see the validation we just did */
20635 CommandCounterIncrement();
20637 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
20638 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
20639 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
20640 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
20641 Assert(!parentIdx
->rd_index
->indisvalid
);
20643 validatePartitionedIndex(parentIdx
, parentTbl
);
20645 relation_close(parentIdx
, AccessExclusiveLock
);
20646 relation_close(parentTbl
, AccessExclusiveLock
);
20651 * When attaching an index as a partition of a partitioned index which is a
20652 * primary key, verify that all the columns in the partition are marked NOT
20656 verifyPartitionIndexNotNull(IndexInfo
*iinfo
, Relation partition
)
20658 for (int i
= 0; i
< iinfo
->ii_NumIndexKeyAttrs
; i
++)
20660 Form_pg_attribute att
= TupleDescAttr(RelationGetDescr(partition
),
20661 iinfo
->ii_IndexAttrNumbers
[i
] - 1);
20663 if (!att
->attnotnull
)
20665 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
20666 errmsg("invalid primary key definition"),
20667 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20668 NameStr(att
->attname
),
20669 RelationGetRelationName(partition
)));
20674 * Return an OID list of constraints that reference the given relation
20675 * that are marked as having a parent constraints.
20678 GetParentedForeignKeyRefs(Relation partition
)
20680 Relation pg_constraint
;
20683 ScanKeyData key
[2];
20684 List
*constraints
= NIL
;
20687 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20690 if (RelationGetIndexList(partition
) == NIL
||
20691 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
20692 INDEX_ATTR_BITMAP_KEY
)))
20695 /* Search for constraints referencing this table */
20696 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
20697 ScanKeyInit(&key
[0],
20698 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
20699 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
20700 ScanKeyInit(&key
[1],
20701 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
20702 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
20704 /* XXX This is a seqscan, as we don't have a usable index */
20705 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
20706 while ((tuple
= systable_getnext(scan
)) != NULL
)
20708 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20711 * We only need to process constraints that are part of larger ones.
20713 if (!OidIsValid(constrForm
->conparentid
))
20716 constraints
= lappend_oid(constraints
, constrForm
->oid
);
20719 systable_endscan(scan
);
20720 table_close(pg_constraint
, AccessShareLock
);
20722 return constraints
;
20726 * During DETACH PARTITION, verify that any foreign keys pointing to the
20727 * partitioned table would not become invalid. An error is raised if any
20728 * referenced values exist.
20731 ATDetachCheckNoForeignKeyRefs(Relation partition
)
20736 constraints
= GetParentedForeignKeyRefs(partition
);
20738 foreach(cell
, constraints
)
20740 Oid constrOid
= lfirst_oid(cell
);
20742 Form_pg_constraint constrForm
;
20744 Trigger trig
= {0};
20746 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
20747 if (!HeapTupleIsValid(tuple
))
20748 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
20749 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
20751 Assert(OidIsValid(constrForm
->conparentid
));
20752 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
20754 /* prevent data changes into the referencing table until commit */
20755 rel
= table_open(constrForm
->conrelid
, ShareLock
);
20757 trig
.tgoid
= InvalidOid
;
20758 trig
.tgname
= NameStr(constrForm
->conname
);
20759 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
20760 trig
.tgisinternal
= true;
20761 trig
.tgconstrrelid
= RelationGetRelid(partition
);
20762 trig
.tgconstrindid
= constrForm
->conindid
;
20763 trig
.tgconstraint
= constrForm
->oid
;
20764 trig
.tgdeferrable
= false;
20765 trig
.tginitdeferred
= false;
20766 /* we needn't fill in remaining fields */
20768 RI_PartitionRemove_Check(&trig
, rel
, partition
);
20770 ReleaseSysCache(tuple
);
20772 table_close(rel
, NoLock
);
20777 * resolve column compression specification to compression method.
20780 GetAttributeCompression(Oid atttypid
, const char *compression
)
20784 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
20785 return InvalidCompressionMethod
;
20788 * To specify a nondefault method, the column data type must be toastable.
20789 * Note this says nothing about whether the column's attstorage setting
20790 * permits compression; we intentionally allow attstorage and
20791 * attcompression to be independent. But with a non-toastable type,
20792 * attstorage could not be set to a value that would permit compression.
20794 * We don't actually need to enforce this, since nothing bad would happen
20795 * if attcompression were non-default; it would never be consulted. But
20796 * it seems more user-friendly to complain about a certainly-useless
20797 * attempt to set the property.
20799 if (!TypeIsToastable(atttypid
))
20801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20802 errmsg("column data type %s does not support compression",
20803 format_type_be(atttypid
))));
20805 cmethod
= CompressionNameToMethod(compression
);
20806 if (!CompressionMethodIsValid(cmethod
))
20808 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20809 errmsg("invalid compression method \"%s\"", compression
)));
20815 * resolve column storage specification
20818 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
20822 if (pg_strcasecmp(storagemode
, "plain") == 0)
20823 cstorage
= TYPSTORAGE_PLAIN
;
20824 else if (pg_strcasecmp(storagemode
, "external") == 0)
20825 cstorage
= TYPSTORAGE_EXTERNAL
;
20826 else if (pg_strcasecmp(storagemode
, "extended") == 0)
20827 cstorage
= TYPSTORAGE_EXTENDED
;
20828 else if (pg_strcasecmp(storagemode
, "main") == 0)
20829 cstorage
= TYPSTORAGE_MAIN
;
20830 else if (pg_strcasecmp(storagemode
, "default") == 0)
20831 cstorage
= get_typstorage(atttypid
);
20834 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20835 errmsg("invalid storage type \"%s\"",
20839 * safety check: do not allow toasted storage modes unless column datatype
20842 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
20844 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20845 errmsg("column data type %s can only have storage PLAIN",
20846 format_type_be(atttypid
))));