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/heapam.h"
20 #include "access/heapam_xlog.h"
21 #include "access/multixact.h"
22 #include "access/reloptions.h"
23 #include "access/relscan.h"
24 #include "access/sysattr.h"
25 #include "access/tableam.h"
26 #include "access/toast_compression.h"
27 #include "access/xact.h"
28 #include "access/xlog.h"
29 #include "access/xloginsert.h"
30 #include "catalog/catalog.h"
31 #include "catalog/heap.h"
32 #include "catalog/index.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_attrdef.h"
38 #include "catalog/pg_collation.h"
39 #include "catalog/pg_constraint.h"
40 #include "catalog/pg_depend.h"
41 #include "catalog/pg_foreign_table.h"
42 #include "catalog/pg_inherits.h"
43 #include "catalog/pg_largeobject.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_opclass.h"
46 #include "catalog/pg_policy.h"
47 #include "catalog/pg_proc.h"
48 #include "catalog/pg_publication_rel.h"
49 #include "catalog/pg_rewrite.h"
50 #include "catalog/pg_statistic_ext.h"
51 #include "catalog/pg_tablespace.h"
52 #include "catalog/pg_trigger.h"
53 #include "catalog/pg_type.h"
54 #include "catalog/storage.h"
55 #include "catalog/storage_xlog.h"
56 #include "catalog/toasting.h"
57 #include "commands/cluster.h"
58 #include "commands/comment.h"
59 #include "commands/defrem.h"
60 #include "commands/event_trigger.h"
61 #include "commands/sequence.h"
62 #include "commands/tablecmds.h"
63 #include "commands/tablespace.h"
64 #include "commands/trigger.h"
65 #include "commands/typecmds.h"
66 #include "commands/user.h"
67 #include "commands/vacuum.h"
68 #include "executor/executor.h"
69 #include "foreign/fdwapi.h"
70 #include "foreign/foreign.h"
71 #include "miscadmin.h"
72 #include "nodes/makefuncs.h"
73 #include "nodes/nodeFuncs.h"
74 #include "nodes/parsenodes.h"
75 #include "optimizer/optimizer.h"
76 #include "parser/parse_coerce.h"
77 #include "parser/parse_collate.h"
78 #include "parser/parse_expr.h"
79 #include "parser/parse_relation.h"
80 #include "parser/parse_type.h"
81 #include "parser/parse_utilcmd.h"
82 #include "parser/parser.h"
83 #include "partitioning/partbounds.h"
84 #include "partitioning/partdesc.h"
86 #include "rewrite/rewriteDefine.h"
87 #include "rewrite/rewriteHandler.h"
88 #include "rewrite/rewriteManip.h"
89 #include "storage/bufmgr.h"
90 #include "storage/lmgr.h"
91 #include "storage/lock.h"
92 #include "storage/predicate.h"
93 #include "storage/smgr.h"
94 #include "tcop/utility.h"
95 #include "utils/acl.h"
96 #include "utils/builtins.h"
97 #include "utils/fmgroids.h"
98 #include "utils/inval.h"
99 #include "utils/lsyscache.h"
100 #include "utils/memutils.h"
101 #include "utils/partcache.h"
102 #include "utils/relcache.h"
103 #include "utils/ruleutils.h"
104 #include "utils/snapmgr.h"
105 #include "utils/syscache.h"
106 #include "utils/timestamp.h"
107 #include "utils/typcache.h"
108 #include "utils/usercontext.h"
111 * ON COMMIT action list
113 typedef struct OnCommitItem
115 Oid relid
; /* relid of relation */
116 OnCommitAction oncommit
; /* what to do at end of xact */
119 * If this entry was created during the current transaction,
120 * creating_subid is the ID of the creating subxact; if created in a prior
121 * transaction, creating_subid is zero. If deleted during the current
122 * transaction, deleting_subid is the ID of the deleting subxact; if no
123 * deletion request is pending, deleting_subid is zero.
125 SubTransactionId creating_subid
;
126 SubTransactionId deleting_subid
;
129 static List
*on_commits
= NIL
;
133 * State information for ALTER TABLE
135 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
136 * structs, one for each table modified by the operation (the named table
137 * plus any child tables that are affected). We save lists of subcommands
138 * to apply to this table (possibly modified by parse transformation steps);
139 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
140 * necessary information is stored in the constraints and newvals lists.
142 * Phase 2 is divided into multiple passes; subcommands are executed in
143 * a pass determined by subcommand type.
146 typedef enum AlterTablePass
148 AT_PASS_UNSET
= -1, /* UNSET will cause ERROR */
149 AT_PASS_DROP
, /* DROP (all flavors) */
150 AT_PASS_ALTER_TYPE
, /* ALTER COLUMN TYPE */
151 AT_PASS_ADD_COL
, /* ADD COLUMN */
152 AT_PASS_SET_EXPRESSION
, /* ALTER SET EXPRESSION */
153 AT_PASS_OLD_INDEX
, /* re-add existing indexes */
154 AT_PASS_OLD_CONSTR
, /* re-add existing constraints */
155 /* We could support a RENAME COLUMN pass here, but not currently used */
156 AT_PASS_ADD_CONSTR
, /* ADD constraints (initial examination) */
157 AT_PASS_COL_ATTRS
, /* set column attributes, eg NOT NULL */
158 AT_PASS_ADD_INDEXCONSTR
, /* ADD index-based constraints */
159 AT_PASS_ADD_INDEX
, /* ADD indexes */
160 AT_PASS_ADD_OTHERCONSTR
, /* ADD other constraints, defaults */
161 AT_PASS_MISC
, /* other stuff */
164 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
166 typedef struct AlteredTableInfo
168 /* Information saved before any work commences: */
169 Oid relid
; /* Relation to work on */
170 char relkind
; /* Its relkind */
171 TupleDesc oldDesc
; /* Pre-modification tuple descriptor */
174 * Transiently set during Phase 2, normally set to NULL.
176 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
177 * returns control. This can be exploited by ATExecCmd subroutines to
178 * close/reopen across transaction boundaries.
182 /* Information saved by Phase 1 for Phase 2: */
183 List
*subcmds
[AT_NUM_PASSES
]; /* Lists of AlterTableCmd */
184 /* Information saved by Phases 1/2 for Phase 3: */
185 List
*constraints
; /* List of NewConstraint */
186 List
*newvals
; /* List of NewColumnValue */
187 List
*afterStmts
; /* List of utility command parsetrees */
188 bool verify_new_notnull
; /* T if we should recheck NOT NULL */
189 int rewrite
; /* Reason for forced rewrite, if any */
190 bool chgAccessMethod
; /* T if SET ACCESS METHOD is used */
191 Oid newAccessMethod
; /* new access method; 0 means no change,
192 * if above is true */
193 Oid newTableSpace
; /* new tablespace; 0 means no change */
194 bool chgPersistence
; /* T if SET LOGGED/UNLOGGED is used */
195 char newrelpersistence
; /* if above is true */
196 Expr
*partition_constraint
; /* for attach partition validation */
197 /* true, if validating default due to some other attach/detach */
198 bool validate_default
;
199 /* Objects to rebuild after completing ALTER TYPE operations */
200 List
*changedConstraintOids
; /* OIDs of constraints to rebuild */
201 List
*changedConstraintDefs
; /* string definitions of same */
202 List
*changedIndexOids
; /* OIDs of indexes to rebuild */
203 List
*changedIndexDefs
; /* string definitions of same */
204 char *replicaIdentityIndex
; /* index to reset as REPLICA IDENTITY */
205 char *clusterOnIndex
; /* index to use for CLUSTER */
206 List
*changedStatisticsOids
; /* OIDs of statistics to rebuild */
207 List
*changedStatisticsDefs
; /* string definitions of same */
210 /* Struct describing one new constraint to check in Phase 3 scan */
211 /* Note: new not-null constraints are handled elsewhere */
212 typedef struct NewConstraint
214 char *name
; /* Constraint name, or NULL if none */
215 ConstrType contype
; /* CHECK or FOREIGN */
216 Oid refrelid
; /* PK rel, if FOREIGN */
217 Oid refindid
; /* OID of PK's index, if FOREIGN */
218 Oid conid
; /* OID of pg_constraint entry, if FOREIGN */
219 Node
*qual
; /* Check expr or CONSTR_FOREIGN Constraint */
220 ExprState
*qualstate
; /* Execution state for CHECK expr */
224 * Struct describing one new column value that needs to be computed during
225 * Phase 3 copy (this could be either a new column with a non-null default, or
226 * a column that we're changing the type of). Columns without such an entry
227 * are just copied from the old table during ATRewriteTable. Note that the
228 * expr is an expression over *old* table values, except when is_generated
229 * is true; then it is an expression over columns of the *new* tuple.
231 typedef struct NewColumnValue
233 AttrNumber attnum
; /* which column */
234 Expr
*expr
; /* expression to compute */
235 ExprState
*exprstate
; /* execution state */
236 bool is_generated
; /* is it a GENERATED expression? */
240 * Error-reporting support for RemoveRelations
242 struct dropmsgstrings
245 int nonexistent_code
;
246 const char *nonexistent_msg
;
247 const char *skipping_msg
;
248 const char *nota_msg
;
249 const char *drophint_msg
;
252 static const struct dropmsgstrings dropmsgstringarray
[] = {
254 ERRCODE_UNDEFINED_TABLE
,
255 gettext_noop("table \"%s\" does not exist"),
256 gettext_noop("table \"%s\" does not exist, skipping"),
257 gettext_noop("\"%s\" is not a table"),
258 gettext_noop("Use DROP TABLE to remove a table.")},
260 ERRCODE_UNDEFINED_TABLE
,
261 gettext_noop("sequence \"%s\" does not exist"),
262 gettext_noop("sequence \"%s\" does not exist, skipping"),
263 gettext_noop("\"%s\" is not a sequence"),
264 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
266 ERRCODE_UNDEFINED_TABLE
,
267 gettext_noop("view \"%s\" does not exist"),
268 gettext_noop("view \"%s\" does not exist, skipping"),
269 gettext_noop("\"%s\" is not a view"),
270 gettext_noop("Use DROP VIEW to remove a view.")},
272 ERRCODE_UNDEFINED_TABLE
,
273 gettext_noop("materialized view \"%s\" does not exist"),
274 gettext_noop("materialized view \"%s\" does not exist, skipping"),
275 gettext_noop("\"%s\" is not a materialized view"),
276 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
278 ERRCODE_UNDEFINED_OBJECT
,
279 gettext_noop("index \"%s\" does not exist"),
280 gettext_noop("index \"%s\" does not exist, skipping"),
281 gettext_noop("\"%s\" is not an index"),
282 gettext_noop("Use DROP INDEX to remove an index.")},
283 {RELKIND_COMPOSITE_TYPE
,
284 ERRCODE_UNDEFINED_OBJECT
,
285 gettext_noop("type \"%s\" does not exist"),
286 gettext_noop("type \"%s\" does not exist, skipping"),
287 gettext_noop("\"%s\" is not a type"),
288 gettext_noop("Use DROP TYPE to remove a type.")},
289 {RELKIND_FOREIGN_TABLE
,
290 ERRCODE_UNDEFINED_OBJECT
,
291 gettext_noop("foreign table \"%s\" does not exist"),
292 gettext_noop("foreign table \"%s\" does not exist, skipping"),
293 gettext_noop("\"%s\" is not a foreign table"),
294 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
295 {RELKIND_PARTITIONED_TABLE
,
296 ERRCODE_UNDEFINED_TABLE
,
297 gettext_noop("table \"%s\" does not exist"),
298 gettext_noop("table \"%s\" does not exist, skipping"),
299 gettext_noop("\"%s\" is not a table"),
300 gettext_noop("Use DROP TABLE to remove a table.")},
301 {RELKIND_PARTITIONED_INDEX
,
302 ERRCODE_UNDEFINED_OBJECT
,
303 gettext_noop("index \"%s\" does not exist"),
304 gettext_noop("index \"%s\" does not exist, skipping"),
305 gettext_noop("\"%s\" is not an index"),
306 gettext_noop("Use DROP INDEX to remove an index.")},
307 {'\0', 0, NULL
, NULL
, NULL
, NULL
}
310 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
311 struct DropRelationCallbackState
313 /* These fields are set by RemoveRelations: */
314 char expected_relkind
;
315 LOCKMODE heap_lockmode
;
316 /* These fields are state to track which subsidiary locks are held: */
319 /* These fields are passed back by RangeVarCallbackForDropRelation: */
321 char actual_relpersistence
;
324 /* Alter table target-type flags for ATSimplePermissions */
325 #define ATT_TABLE 0x0001
326 #define ATT_VIEW 0x0002
327 #define ATT_MATVIEW 0x0004
328 #define ATT_INDEX 0x0008
329 #define ATT_COMPOSITE_TYPE 0x0010
330 #define ATT_FOREIGN_TABLE 0x0020
331 #define ATT_PARTITIONED_INDEX 0x0040
332 #define ATT_SEQUENCE 0x0080
335 * ForeignTruncateInfo
337 * Information related to truncation of foreign tables. This is used for
338 * the elements in a hash table. It uses the server OID as lookup key,
339 * and includes a per-server list of all foreign tables involved in the
342 typedef struct ForeignTruncateInfo
346 } ForeignTruncateInfo
;
349 * Partition tables are expected to be dropped when the parent partitioned
350 * table gets dropped. Hence for partitioning we use AUTO dependency.
351 * Otherwise, for regular inheritance use NORMAL dependency.
353 #define child_dependency_type(child_is_partition) \
354 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
356 static void truncate_check_rel(Oid relid
, Form_pg_class reltuple
);
357 static void truncate_check_perms(Oid relid
, Form_pg_class reltuple
);
358 static void truncate_check_activity(Relation rel
);
359 static void RangeVarCallbackForTruncate(const RangeVar
*relation
,
360 Oid relId
, Oid oldRelId
, void *arg
);
361 static List
*MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
362 bool is_partition
, List
**supconstr
);
363 static List
*MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
);
364 static void MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
);
365 static ColumnDef
*MergeInheritedAttribute(List
*inh_columns
, int exist_attno
, const ColumnDef
*newdef
);
366 static void MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
);
367 static void MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
);
368 static void StoreCatalogInheritance(Oid relationId
, List
*supers
,
369 bool child_is_partition
);
370 static void StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
371 int32 seqNumber
, Relation inhRelation
,
372 bool child_is_partition
);
373 static int findAttrByName(const char *attributeName
, const List
*columns
);
374 static void AlterIndexNamespaces(Relation classRel
, Relation rel
,
375 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
);
376 static void AlterSeqNamespaces(Relation classRel
, Relation rel
,
377 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
379 static ObjectAddress
ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
,
380 bool recurse
, bool recursing
, LOCKMODE lockmode
);
381 static bool ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
382 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
384 static ObjectAddress
ATExecValidateConstraint(List
**wqueue
,
385 Relation rel
, char *constrName
,
386 bool recurse
, bool recursing
, LOCKMODE lockmode
);
387 static int transformColumnNameList(Oid relId
, List
*colList
,
388 int16
*attnums
, Oid
*atttypids
);
389 static int transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
391 int16
*attnums
, Oid
*atttypids
,
393 static Oid
transformFkeyCheckAttrs(Relation pkrel
,
394 int numattrs
, int16
*attnums
,
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
);
402 static void CheckAlterTableIsSafe(Relation rel
);
403 static void ATController(AlterTableStmt
*parsetree
,
404 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
405 AlterTableUtilityContext
*context
);
406 static void ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
407 bool recurse
, bool recursing
, LOCKMODE lockmode
,
408 AlterTableUtilityContext
*context
);
409 static void ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
410 AlterTableUtilityContext
*context
);
411 static void ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
412 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
413 AlterTableUtilityContext
*context
);
414 static AlterTableCmd
*ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
,
415 Relation rel
, AlterTableCmd
*cmd
,
416 bool recurse
, LOCKMODE lockmode
,
417 AlterTablePass cur_pass
,
418 AlterTableUtilityContext
*context
);
419 static void ATRewriteTables(AlterTableStmt
*parsetree
,
420 List
**wqueue
, LOCKMODE lockmode
,
421 AlterTableUtilityContext
*context
);
422 static void ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
);
423 static AlteredTableInfo
*ATGetQueueEntry(List
**wqueue
, Relation rel
);
424 static void ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
);
425 static void ATSimpleRecursion(List
**wqueue
, Relation rel
,
426 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
427 AlterTableUtilityContext
*context
);
428 static void ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
);
429 static void ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
431 AlterTableUtilityContext
*context
);
432 static List
*find_typed_table_dependencies(Oid typeOid
, const char *typeName
,
433 DropBehavior behavior
);
434 static void ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
435 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
436 AlterTableUtilityContext
*context
);
437 static ObjectAddress
ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
,
438 Relation rel
, AlterTableCmd
**cmd
,
439 bool recurse
, bool recursing
,
440 LOCKMODE lockmode
, AlterTablePass cur_pass
,
441 AlterTableUtilityContext
*context
);
442 static bool check_for_column_name_collision(Relation rel
, const char *colname
,
444 static void add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
);
445 static void add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
);
446 static void ATPrepDropNotNull(Relation rel
, bool recurse
, bool recursing
);
447 static ObjectAddress
ATExecDropNotNull(Relation rel
, const char *colName
, LOCKMODE lockmode
);
448 static void ATPrepSetNotNull(List
**wqueue
, Relation rel
,
449 AlterTableCmd
*cmd
, bool recurse
, bool recursing
,
451 AlterTableUtilityContext
*context
);
452 static ObjectAddress
ATExecSetNotNull(AlteredTableInfo
*tab
, Relation rel
,
453 const char *colName
, LOCKMODE lockmode
);
454 static void ATExecCheckNotNull(AlteredTableInfo
*tab
, Relation rel
,
455 const char *colName
, LOCKMODE lockmode
);
456 static bool NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
);
457 static bool ConstraintImpliedByRelConstraint(Relation scanrel
,
458 List
*testConstraint
, List
*provenConstraint
);
459 static ObjectAddress
ATExecColumnDefault(Relation rel
, const char *colName
,
460 Node
*newDefault
, LOCKMODE lockmode
);
461 static ObjectAddress
ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
463 static ObjectAddress
ATExecAddIdentity(Relation rel
, const char *colName
,
464 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
465 static ObjectAddress
ATExecSetIdentity(Relation rel
, const char *colName
,
466 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
);
467 static ObjectAddress
ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
468 bool recurse
, bool recursing
);
469 static ObjectAddress
ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
470 Node
*newExpr
, LOCKMODE lockmode
);
471 static void ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
);
472 static ObjectAddress
ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
);
473 static ObjectAddress
ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
,
474 Node
*newValue
, LOCKMODE lockmode
);
475 static ObjectAddress
ATExecSetOptions(Relation rel
, const char *colName
,
476 Node
*options
, bool isReset
, LOCKMODE lockmode
);
477 static ObjectAddress
ATExecSetStorage(Relation rel
, const char *colName
,
478 Node
*newValue
, LOCKMODE lockmode
);
479 static void ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
480 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
481 AlterTableUtilityContext
*context
);
482 static ObjectAddress
ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
483 DropBehavior behavior
,
484 bool recurse
, bool recursing
,
485 bool missing_ok
, LOCKMODE lockmode
,
486 ObjectAddresses
*addrs
);
487 static ObjectAddress
ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
488 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
489 static ObjectAddress
ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
490 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
);
491 static ObjectAddress
ATExecAddConstraint(List
**wqueue
,
492 AlteredTableInfo
*tab
, Relation rel
,
493 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
495 static char *ChooseForeignKeyConstraintNameAddition(List
*colnames
);
496 static ObjectAddress
ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
497 IndexStmt
*stmt
, LOCKMODE lockmode
);
498 static ObjectAddress
ATAddCheckConstraint(List
**wqueue
,
499 AlteredTableInfo
*tab
, Relation rel
,
501 bool recurse
, bool recursing
, bool is_readd
,
503 static ObjectAddress
ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
,
504 Relation rel
, Constraint
*fkconstraint
,
505 bool recurse
, bool recursing
,
507 static ObjectAddress
addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
,
508 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
509 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
510 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
511 int numfkdelsetcols
, int16
*fkdelsetcols
,
513 Oid parentDelTrigger
, Oid parentUpdTrigger
);
514 static void validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
515 int numfksetcols
, const int16
*fksetcolsattnums
,
517 static void addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
,
518 Relation rel
, Relation pkrel
, Oid indexOid
, Oid parentConstr
,
519 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
520 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
521 int numfkdelsetcols
, int16
*fkdelsetcols
,
522 bool old_check_ok
, LOCKMODE lockmode
,
523 Oid parentInsTrigger
, Oid parentUpdTrigger
);
524 static void CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
525 Relation partitionRel
);
526 static void CloneFkReferenced(Relation parentRel
, Relation partitionRel
);
527 static void CloneFkReferencing(List
**wqueue
, Relation parentRel
,
529 static void createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
530 Constraint
*fkconstraint
, Oid constraintOid
,
532 Oid parentInsTrigger
, Oid parentUpdTrigger
,
533 Oid
*insertTrigOid
, Oid
*updateTrigOid
);
534 static void createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
,
535 Constraint
*fkconstraint
, Oid constraintOid
,
537 Oid parentDelTrigger
, Oid parentUpdTrigger
,
538 Oid
*deleteTrigOid
, Oid
*updateTrigOid
);
539 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
541 Oid parentConstrOid
, int numfks
,
542 AttrNumber
*mapped_conkey
, AttrNumber
*confkey
,
544 Oid parentInsTrigger
,
545 Oid parentUpdTrigger
,
547 static void GetForeignKeyActionTriggers(Relation trigrel
,
548 Oid conoid
, Oid confrelid
, Oid conrelid
,
549 Oid
*deleteTriggerOid
,
550 Oid
*updateTriggerOid
);
551 static void GetForeignKeyCheckTriggers(Relation trigrel
,
552 Oid conoid
, Oid confrelid
, Oid conrelid
,
553 Oid
*insertTriggerOid
,
554 Oid
*updateTriggerOid
);
555 static void ATExecDropConstraint(Relation rel
, const char *constrName
,
556 DropBehavior behavior
,
557 bool recurse
, bool recursing
,
558 bool missing_ok
, LOCKMODE lockmode
);
559 static void ATPrepAlterColumnType(List
**wqueue
,
560 AlteredTableInfo
*tab
, Relation rel
,
561 bool recurse
, bool recursing
,
562 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
563 AlterTableUtilityContext
*context
);
564 static bool ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
);
565 static ObjectAddress
ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
566 AlterTableCmd
*cmd
, LOCKMODE lockmode
);
567 static void RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
568 Relation rel
, AttrNumber attnum
, const char *colName
);
569 static void RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
);
570 static void RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
);
571 static void RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
);
572 static void ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
,
574 static void ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
,
575 char *cmd
, List
**wqueue
, LOCKMODE lockmode
,
577 static void RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
,
578 Oid objid
, Relation rel
, List
*domname
,
579 const char *conname
);
580 static void TryReuseIndex(Oid oldId
, IndexStmt
*stmt
);
581 static void TryReuseForeignKey(Oid oldId
, Constraint
*con
);
582 static ObjectAddress
ATExecAlterColumnGenericOptions(Relation rel
, const char *colName
,
583 List
*options
, LOCKMODE lockmode
);
584 static void change_owner_fix_column_acls(Oid relationOid
,
585 Oid oldOwnerId
, Oid newOwnerId
);
586 static void change_owner_recurse_to_sequences(Oid relationOid
,
587 Oid newOwnerId
, LOCKMODE lockmode
);
588 static ObjectAddress
ATExecClusterOn(Relation rel
, const char *indexName
,
590 static void ATExecDropCluster(Relation rel
, LOCKMODE lockmode
);
591 static void ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
);
592 static void ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
);
593 static bool ATPrepChangePersistence(Relation rel
, bool toLogged
);
594 static void ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
,
595 const char *tablespacename
, LOCKMODE lockmode
);
596 static void ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
);
597 static void ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
);
598 static void ATExecSetRelOptions(Relation rel
, List
*defList
,
599 AlterTableType operation
,
601 static void ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
602 char fires_when
, bool skip_system
, bool recurse
,
604 static void ATExecEnableDisableRule(Relation rel
, const char *rulename
,
605 char fires_when
, LOCKMODE lockmode
);
606 static void ATPrepAddInherit(Relation child_rel
);
607 static ObjectAddress
ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
);
608 static ObjectAddress
ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
);
609 static void drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
610 DependencyType deptype
);
611 static ObjectAddress
ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
);
612 static void ATExecDropOf(Relation rel
, LOCKMODE lockmode
);
613 static void ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
);
614 static void ATExecGenericOptions(Relation rel
, List
*options
);
615 static void ATExecSetRowSecurity(Relation rel
, bool rls
);
616 static void ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
);
617 static ObjectAddress
ATExecSetCompression(Relation rel
,
618 const char *column
, Node
*newValue
, LOCKMODE lockmode
);
620 static void index_copy_data(Relation rel
, RelFileLocator newrlocator
);
621 static const char *storage_name(char c
);
623 static void RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
,
624 Oid oldRelOid
, void *arg
);
625 static void RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
,
626 Oid oldrelid
, void *arg
);
627 static PartitionSpec
*transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
);
628 static void ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
629 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
630 PartitionStrategy strategy
);
631 static void CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
);
632 static void RemoveInheritance(Relation child_rel
, Relation parent_rel
,
633 bool expect_detached
);
634 static ObjectAddress
ATExecAttachPartition(List
**wqueue
, Relation rel
,
636 AlterTableUtilityContext
*context
);
637 static void AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
);
638 static void QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
639 List
*partConstraint
,
640 bool validate_default
);
641 static void CloneRowTriggersToPartition(Relation parent
, Relation partition
);
642 static void DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
);
643 static void DropClonedTriggersFromPartition(Oid partitionId
);
644 static ObjectAddress
ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
,
645 Relation rel
, RangeVar
*name
,
647 static void DetachPartitionFinalize(Relation rel
, Relation partRel
,
648 bool concurrent
, Oid defaultPartOid
);
649 static ObjectAddress
ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
);
650 static ObjectAddress
ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
,
652 static void validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
);
653 static void refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
,
654 Relation partitionTbl
);
655 static List
*GetParentedForeignKeyRefs(Relation partition
);
656 static void ATDetachCheckNoForeignKeyRefs(Relation partition
);
657 static char GetAttributeCompression(Oid atttypid
, const char *compression
);
658 static char GetAttributeStorage(Oid atttypid
, const char *storagemode
);
660 static void ATExecSplitPartition(List
**wqueue
, AlteredTableInfo
*tab
,
661 Relation rel
, PartitionCmd
*cmd
,
662 AlterTableUtilityContext
*context
);
663 static void ATExecMergePartitions(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
664 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
);
666 /* ----------------------------------------------------------------
668 * Creates a new relation.
670 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
671 * The other arguments are used to extend the behavior for other cases:
672 * relkind: relkind to assign to the new relation
673 * ownerId: if not InvalidOid, use this as the new relation's owner.
674 * typaddress: if not null, it's set to the pg_type entry's address.
675 * queryString: for error reporting
677 * Note that permissions checks are done against current user regardless of
678 * ownerId. A nonzero ownerId is used when someone is creating a relation
679 * "on behalf of" someone else, so we still want to see that the current user
680 * has permissions to do it.
682 * If successful, returns the address of the new relation.
683 * ----------------------------------------------------------------
686 DefineRelation(CreateStmt
*stmt
, char relkind
, Oid ownerId
,
687 ObjectAddress
*typaddress
, const char *queryString
)
689 char relname
[NAMEDATALEN
];
694 TupleDesc descriptor
;
696 List
*old_constraints
;
698 List
*cookedDefaults
;
703 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
705 ObjectAddress address
;
706 LOCKMODE parentLockmode
;
707 Oid accessMethodId
= InvalidOid
;
710 * Truncate relname to appropriate length (probably a waste of time, as
711 * parser should have done this already).
713 strlcpy(relname
, stmt
->relation
->relname
, NAMEDATALEN
);
716 * Check consistency of arguments
718 if (stmt
->oncommit
!= ONCOMMIT_NOOP
719 && stmt
->relation
->relpersistence
!= RELPERSISTENCE_TEMP
)
721 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
722 errmsg("ON COMMIT can only be used on temporary tables")));
724 if (stmt
->partspec
!= NULL
)
726 if (relkind
!= RELKIND_RELATION
)
727 elog(ERROR
, "unexpected relkind: %d", (int) relkind
);
729 relkind
= RELKIND_PARTITIONED_TABLE
;
736 * Look up the namespace in which we are supposed to create the relation,
737 * check we have permission to create there, lock it against concurrent
738 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
739 * namespace is selected.
742 RangeVarGetAndCheckCreationNamespace(stmt
->relation
, NoLock
, NULL
);
745 * Security check: disallow creating temp tables from security-restricted
746 * code. This is needed because calling code might not expect untrusted
747 * tables to appear in pg_temp at the front of its search path.
749 if (stmt
->relation
->relpersistence
== RELPERSISTENCE_TEMP
750 && InSecurityRestrictedOperation())
752 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
753 errmsg("cannot create temporary table within security-restricted operation")));
756 * Determine the lockmode to use when scanning parents. A self-exclusive
757 * lock is needed here.
759 * For regular inheritance, if two backends attempt to add children to the
760 * same parent simultaneously, and that parent has no pre-existing
761 * children, then both will attempt to update the parent's relhassubclass
762 * field, leading to a "tuple concurrently updated" error. Also, this
763 * interlocks against a concurrent ANALYZE on the parent table, which
764 * might otherwise be attempting to clear the parent's relhassubclass
765 * field, if its previous children were recently dropped.
767 * If the child table is a partition, then we instead grab an exclusive
768 * lock on the parent because its partition descriptor will be changed by
769 * addition of the new partition.
771 parentLockmode
= (stmt
->partbound
!= NULL
? AccessExclusiveLock
:
772 ShareUpdateExclusiveLock
);
774 /* Determine the list of OIDs of the parents. */
776 foreach(listptr
, stmt
->inhRelations
)
778 RangeVar
*rv
= (RangeVar
*) lfirst(listptr
);
781 parentOid
= RangeVarGetRelid(rv
, parentLockmode
, false);
784 * Reject duplications in the list of parents.
786 if (list_member_oid(inheritOids
, parentOid
))
788 (errcode(ERRCODE_DUPLICATE_TABLE
),
789 errmsg("relation \"%s\" would be inherited from more than once",
790 get_rel_name(parentOid
))));
792 inheritOids
= lappend_oid(inheritOids
, parentOid
);
796 * Select tablespace to use: an explicitly indicated one, or (in the case
797 * of a partitioned table) the parent's, if it has one.
799 if (stmt
->tablespacename
)
801 tablespaceId
= get_tablespace_oid(stmt
->tablespacename
, false);
803 if (partitioned
&& tablespaceId
== MyDatabaseTableSpace
)
805 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
806 errmsg("cannot specify default tablespace for partitioned relations")));
808 else if (stmt
->partbound
)
810 Assert(list_length(inheritOids
) == 1);
811 tablespaceId
= get_rel_tablespace(linitial_oid(inheritOids
));
814 tablespaceId
= InvalidOid
;
816 /* still nothing? use the default */
817 if (!OidIsValid(tablespaceId
))
818 tablespaceId
= GetDefaultTablespace(stmt
->relation
->relpersistence
,
821 /* Check permissions except when using database's default */
822 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
826 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(),
828 if (aclresult
!= ACLCHECK_OK
)
829 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
830 get_tablespace_name(tablespaceId
));
833 /* In all cases disallow placing user relations in pg_global */
834 if (tablespaceId
== GLOBALTABLESPACE_OID
)
836 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
837 errmsg("only shared relations can be placed in pg_global tablespace")));
839 /* Identify user ID that will own the table */
840 if (!OidIsValid(ownerId
))
841 ownerId
= GetUserId();
844 * Parse and validate reloptions, if any.
846 reloptions
= transformRelOptions((Datum
) 0, stmt
->options
, NULL
, validnsps
,
852 (void) view_reloptions(reloptions
, true);
854 case RELKIND_PARTITIONED_TABLE
:
855 (void) partitioned_table_reloptions(reloptions
, true);
858 (void) heap_reloptions(relkind
, reloptions
, true);
861 if (stmt
->ofTypename
)
865 ofTypeId
= typenameTypeId(NULL
, stmt
->ofTypename
);
867 aclresult
= object_aclcheck(TypeRelationId
, ofTypeId
, GetUserId(), ACL_USAGE
);
868 if (aclresult
!= ACLCHECK_OK
)
869 aclcheck_error_type(aclresult
, ofTypeId
);
872 ofTypeId
= InvalidOid
;
875 * Look up inheritance ancestors and generate relation schema, including
876 * inherited attributes. (Note that stmt->tableElts is destructively
877 * modified by MergeAttributes.)
880 MergeAttributes(stmt
->tableElts
, inheritOids
,
881 stmt
->relation
->relpersistence
,
882 stmt
->partbound
!= NULL
,
886 * Create a tuple descriptor from the relation schema. Note that this
887 * deals with column names, types, and not-null constraints, but not
888 * default values or CHECK constraints; we handle those below.
890 descriptor
= BuildDescForRelation(stmt
->tableElts
);
893 * Find columns with default values and prepare for insertion of the
894 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
895 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
896 * while raw defaults go into a list of RawColumnDefault structs that will
897 * be processed by AddRelationNewConstraints. (We can't deal with raw
898 * expressions until we can do transformExpr.)
900 * We can set the atthasdef flags now in the tuple descriptor; this just
901 * saves StoreAttrDefault from having to do an immediate update of the
905 cookedDefaults
= NIL
;
908 foreach(listptr
, stmt
->tableElts
)
910 ColumnDef
*colDef
= lfirst(listptr
);
911 Form_pg_attribute attr
;
914 attr
= TupleDescAttr(descriptor
, attnum
- 1);
916 if (colDef
->raw_default
!= NULL
)
918 RawColumnDefault
*rawEnt
;
920 Assert(colDef
->cooked_default
== NULL
);
922 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
923 rawEnt
->attnum
= attnum
;
924 rawEnt
->raw_default
= colDef
->raw_default
;
925 rawEnt
->missingMode
= false;
926 rawEnt
->generated
= colDef
->generated
;
927 rawDefaults
= lappend(rawDefaults
, rawEnt
);
928 attr
->atthasdef
= true;
930 else if (colDef
->cooked_default
!= NULL
)
932 CookedConstraint
*cooked
;
934 cooked
= (CookedConstraint
*) palloc(sizeof(CookedConstraint
));
935 cooked
->contype
= CONSTR_DEFAULT
;
936 cooked
->conoid
= InvalidOid
; /* until created */
938 cooked
->attnum
= attnum
;
939 cooked
->expr
= colDef
->cooked_default
;
940 cooked
->skip_validation
= false;
941 cooked
->is_local
= true; /* not used for defaults */
942 cooked
->inhcount
= 0; /* ditto */
943 cooked
->is_no_inherit
= false;
944 cookedDefaults
= lappend(cookedDefaults
, cooked
);
945 attr
->atthasdef
= true;
950 * For relations with table AM and partitioned tables, select access
951 * method to use: an explicitly indicated one, or (in the case of a
952 * partitioned table) the parent's, if it has one.
954 if (stmt
->accessMethod
!= NULL
)
956 Assert(RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
);
957 accessMethodId
= get_table_am_oid(stmt
->accessMethod
, false);
959 else if (RELKIND_HAS_TABLE_AM(relkind
) || relkind
== RELKIND_PARTITIONED_TABLE
)
963 Assert(list_length(inheritOids
) == 1);
964 accessMethodId
= get_rel_relam(linitial_oid(inheritOids
));
967 if (RELKIND_HAS_TABLE_AM(relkind
) && !OidIsValid(accessMethodId
))
968 accessMethodId
= get_table_am_oid(default_table_access_method
, false);
972 * Create the relation. Inherited defaults and constraints are passed in
973 * for immediate handling --- since they don't need parsing, they can be
974 * stored immediately.
976 relationId
= heap_create_with_catalog(relname
,
985 list_concat(cookedDefaults
,
988 stmt
->relation
->relpersistence
,
994 allowSystemTableMods
,
1000 * We must bump the command counter to make the newly-created relation
1001 * tuple visible for opening.
1003 CommandCounterIncrement();
1006 * Open the new relation and acquire exclusive lock on it. This isn't
1007 * really necessary for locking out other backends (since they can't see
1008 * the new rel anyway until we commit), but it keeps the lock manager from
1009 * complaining about deadlock risks.
1011 rel
= relation_open(relationId
, AccessExclusiveLock
);
1014 * Now add any newly specified column default and generation expressions
1015 * to the new relation. These are passed to us in the form of raw
1016 * parsetrees; we need to transform them to executable expression trees
1017 * before they can be added. The most convenient way to do that is to
1018 * apply the parser's transformExpr routine, but transformExpr doesn't
1019 * work unless we have a pre-existing relation. So, the transformation has
1020 * to be postponed to this final step of CREATE TABLE.
1022 * This needs to be before processing the partitioning clauses because
1023 * those could refer to generated columns.
1026 AddRelationNewConstraints(rel
, rawDefaults
, NIL
,
1027 true, true, false, queryString
);
1030 * Make column generation expressions visible for use by partitioning.
1032 CommandCounterIncrement();
1034 /* Process and store partition bound, if any. */
1035 if (stmt
->partbound
)
1037 PartitionBoundSpec
*bound
;
1039 Oid parentId
= linitial_oid(inheritOids
),
1043 ParseNamespaceItem
*nsitem
;
1045 /* Already have strong enough lock on the parent */
1046 parent
= table_open(parentId
, NoLock
);
1049 * We are going to try to validate the partition bound specification
1050 * against the partition key of parentRel, so it better have one.
1052 if (parent
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
1054 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1055 errmsg("\"%s\" is not partitioned",
1056 RelationGetRelationName(parent
))));
1059 * The partition constraint of the default partition depends on the
1060 * partition bounds of every other partition. It is possible that
1061 * another backend might be about to execute a query on the default
1062 * partition table, and that the query relies on previously cached
1063 * default partition constraints. We must therefore take a table lock
1064 * strong enough to prevent all queries on the default partition from
1065 * proceeding until we commit and send out a shared-cache-inval notice
1066 * that will make them update their index lists.
1068 * Order of locking: The relation being added won't be visible to
1069 * other backends until it is committed, hence here in
1070 * DefineRelation() the order of locking the default partition and the
1071 * relation being added does not matter. But at all other places we
1072 * need to lock the default relation before we lock the relation being
1073 * added or removed i.e. we should take the lock in same order at all
1074 * the places such that lock parent, lock default partition and then
1075 * lock the partition so as to avoid a deadlock.
1078 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent
,
1080 if (OidIsValid(defaultPartOid
))
1081 defaultRel
= table_open(defaultPartOid
, AccessExclusiveLock
);
1083 /* Transform the bound values */
1084 pstate
= make_parsestate(NULL
);
1085 pstate
->p_sourcetext
= queryString
;
1088 * Add an nsitem containing this relation, so that transformExpr
1089 * called on partition bound expressions is able to report errors
1090 * using a proper context.
1092 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
1093 NULL
, false, false);
1094 addNSItemToQuery(pstate
, nsitem
, false, true, true);
1096 bound
= transformPartitionBound(pstate
, parent
, stmt
->partbound
);
1099 * Check first that the new partition's bound is valid and does not
1100 * overlap with any of existing partitions of the parent.
1102 check_new_partition_bound(relname
, parent
, bound
, pstate
);
1105 * If the default partition exists, its partition constraints will
1106 * change after the addition of this new partition such that it won't
1107 * allow any row that qualifies for this new partition. So, check that
1108 * the existing data in the default partition satisfies the constraint
1109 * as it will exist after adding this partition.
1111 if (OidIsValid(defaultPartOid
))
1113 check_default_partition_contents(parent
, defaultRel
, bound
);
1114 /* Keep the lock until commit. */
1115 table_close(defaultRel
, NoLock
);
1118 /* Update the pg_class entry. */
1119 StorePartitionBound(rel
, parent
, bound
);
1121 table_close(parent
, NoLock
);
1124 /* Store inheritance information for new rel. */
1125 StoreCatalogInheritance(relationId
, inheritOids
, stmt
->partbound
!= NULL
);
1128 * Process the partitioning specification (if any) and store the partition
1129 * key information into the catalog.
1135 AttrNumber partattrs
[PARTITION_MAX_KEYS
];
1136 Oid partopclass
[PARTITION_MAX_KEYS
];
1137 Oid partcollation
[PARTITION_MAX_KEYS
];
1138 List
*partexprs
= NIL
;
1140 pstate
= make_parsestate(NULL
);
1141 pstate
->p_sourcetext
= queryString
;
1143 partnatts
= list_length(stmt
->partspec
->partParams
);
1145 /* Protect fixed-size arrays here and in executor */
1146 if (partnatts
> PARTITION_MAX_KEYS
)
1148 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
1149 errmsg("cannot partition using more than %d columns",
1150 PARTITION_MAX_KEYS
)));
1153 * We need to transform the raw parsetrees corresponding to partition
1154 * expressions into executable expression trees. Like column defaults
1155 * and CHECK constraints, we could not have done the transformation
1158 stmt
->partspec
= transformPartitionSpec(rel
, stmt
->partspec
);
1160 ComputePartitionAttrs(pstate
, rel
, stmt
->partspec
->partParams
,
1161 partattrs
, &partexprs
, partopclass
,
1162 partcollation
, stmt
->partspec
->strategy
);
1164 StorePartitionKey(rel
, stmt
->partspec
->strategy
, partnatts
, partattrs
,
1166 partopclass
, partcollation
);
1168 /* make it all visible */
1169 CommandCounterIncrement();
1173 * If we're creating a partition, create now all the indexes, triggers,
1174 * FKs defined in the parent.
1176 * We can't do it earlier, because DefineIndex wants to know the partition
1177 * key which we just stored.
1179 if (stmt
->partbound
)
1181 Oid parentId
= linitial_oid(inheritOids
);
1186 /* Already have strong enough lock on the parent */
1187 parent
= table_open(parentId
, NoLock
);
1188 idxlist
= RelationGetIndexList(parent
);
1191 * For each index in the parent table, create one in the partition
1193 foreach(cell
, idxlist
)
1195 Relation idxRel
= index_open(lfirst_oid(cell
), AccessShareLock
);
1200 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
1202 if (idxRel
->rd_index
->indisunique
)
1204 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1205 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1206 RelationGetRelationName(parent
)),
1207 errdetail("Table \"%s\" contains indexes that are unique.",
1208 RelationGetRelationName(parent
))));
1211 index_close(idxRel
, AccessShareLock
);
1216 attmap
= build_attrmap_by_name(RelationGetDescr(rel
),
1217 RelationGetDescr(parent
),
1220 generateClonedIndexStmt(NULL
, idxRel
,
1221 attmap
, &constraintOid
);
1222 DefineIndex(RelationGetRelid(rel
),
1225 RelationGetRelid(idxRel
),
1228 false, false, false, false, false);
1230 index_close(idxRel
, AccessShareLock
);
1236 * If there are any row-level triggers, clone them to the new
1239 if (parent
->trigdesc
!= NULL
)
1240 CloneRowTriggersToPartition(parent
, rel
);
1243 * And foreign keys too. Note that because we're freshly creating the
1244 * table, there is no need to verify these new constraints.
1246 CloneForeignKeyConstraints(NULL
, parent
, rel
);
1248 table_close(parent
, NoLock
);
1252 * Now add any newly specified CHECK constraints to the new relation. Same
1253 * as for defaults above, but these need to come after partitioning is set
1256 if (stmt
->constraints
)
1257 AddRelationNewConstraints(rel
, NIL
, stmt
->constraints
,
1258 true, true, false, queryString
);
1260 ObjectAddressSet(address
, RelationRelationId
, relationId
);
1263 * Clean up. We keep lock on new relation (although it shouldn't be
1264 * visible to anyone else anyway, until commit).
1266 relation_close(rel
, NoLock
);
1272 * BuildDescForRelation
1274 * Given a list of ColumnDef nodes, build a TupleDesc.
1276 * Note: This is only for the limited purpose of table and view creation. Not
1277 * everything is filled in. A real tuple descriptor should be obtained from
1281 BuildDescForRelation(const List
*columns
)
1294 * allocate a new tuple descriptor
1296 natts
= list_length(columns
);
1297 desc
= CreateTemplateTupleDesc(natts
);
1303 ColumnDef
*entry
= lfirst(l
);
1304 AclResult aclresult
;
1305 Form_pg_attribute att
;
1308 * for each entry in the list, get the name and type information from
1309 * the list and have TupleDescInitEntry fill in the attribute
1310 * information we need.
1314 attname
= entry
->colname
;
1315 typenameTypeIdAndMod(NULL
, entry
->typeName
, &atttypid
, &atttypmod
);
1317 aclresult
= object_aclcheck(TypeRelationId
, atttypid
, GetUserId(), ACL_USAGE
);
1318 if (aclresult
!= ACLCHECK_OK
)
1319 aclcheck_error_type(aclresult
, atttypid
);
1321 attcollation
= GetColumnDefCollation(NULL
, entry
, atttypid
);
1322 attdim
= list_length(entry
->typeName
->arrayBounds
);
1323 if (attdim
> PG_INT16_MAX
)
1325 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1326 errmsg("too many array dimensions"));
1328 if (entry
->typeName
->setof
)
1330 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
1331 errmsg("column \"%s\" cannot be declared SETOF",
1334 TupleDescInitEntry(desc
, attnum
, attname
,
1335 atttypid
, atttypmod
, attdim
);
1336 att
= TupleDescAttr(desc
, attnum
- 1);
1338 /* Override TupleDescInitEntry's settings as requested */
1339 TupleDescInitEntryCollation(desc
, attnum
, attcollation
);
1341 /* Fill in additional stuff not handled by TupleDescInitEntry */
1342 att
->attnotnull
= entry
->is_not_null
;
1343 att
->attislocal
= entry
->is_local
;
1344 att
->attinhcount
= entry
->inhcount
;
1345 att
->attidentity
= entry
->identity
;
1346 att
->attgenerated
= entry
->generated
;
1347 att
->attcompression
= GetAttributeCompression(att
->atttypid
, entry
->compression
);
1349 att
->attstorage
= entry
->storage
;
1350 else if (entry
->storage_name
)
1351 att
->attstorage
= GetAttributeStorage(att
->atttypid
, entry
->storage_name
);
1358 * Emit the right error or warning message for a "DROP" command issued on a
1359 * non-existent relation
1362 DropErrorMsgNonExistent(RangeVar
*rel
, char rightkind
, bool missing_ok
)
1364 const struct dropmsgstrings
*rentry
;
1366 if (rel
->schemaname
!= NULL
&&
1367 !OidIsValid(LookupNamespaceNoError(rel
->schemaname
)))
1372 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1373 errmsg("schema \"%s\" does not exist", rel
->schemaname
)));
1378 (errmsg("schema \"%s\" does not exist, skipping",
1384 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1386 if (rentry
->kind
== rightkind
)
1391 (errcode(rentry
->nonexistent_code
),
1392 errmsg(rentry
->nonexistent_msg
, rel
->relname
)));
1396 ereport(NOTICE
, (errmsg(rentry
->skipping_msg
, rel
->relname
)));
1402 Assert(rentry
->kind
!= '\0'); /* Should be impossible */
1406 * Emit the right error message for a "DROP" command issued on a
1407 * relation of the wrong type
1410 DropErrorMsgWrongType(const char *relname
, char wrongkind
, char rightkind
)
1412 const struct dropmsgstrings
*rentry
;
1413 const struct dropmsgstrings
*wentry
;
1415 for (rentry
= dropmsgstringarray
; rentry
->kind
!= '\0'; rentry
++)
1416 if (rentry
->kind
== rightkind
)
1418 Assert(rentry
->kind
!= '\0');
1420 for (wentry
= dropmsgstringarray
; wentry
->kind
!= '\0'; wentry
++)
1421 if (wentry
->kind
== wrongkind
)
1423 /* wrongkind could be something we don't have in our table... */
1426 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1427 errmsg(rentry
->nota_msg
, relname
),
1428 (wentry
->kind
!= '\0') ? errhint("%s", _(wentry
->drophint_msg
)) : 0));
1433 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1434 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1437 RemoveRelations(DropStmt
*drop
)
1439 ObjectAddresses
*objects
;
1443 LOCKMODE lockmode
= AccessExclusiveLock
;
1445 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1446 if (drop
->concurrent
)
1449 * Note that for temporary relations this lock may get upgraded later
1450 * on, but as no other session can access a temporary relation, this
1453 lockmode
= ShareUpdateExclusiveLock
;
1454 Assert(drop
->removeType
== OBJECT_INDEX
);
1455 if (list_length(drop
->objects
) != 1)
1457 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1458 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1459 if (drop
->behavior
== DROP_CASCADE
)
1461 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1462 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1466 * First we identify all the relations, then we delete them in a single
1467 * performMultipleDeletions() call. This is to avoid unwanted DROP
1468 * RESTRICT errors if one of the relations depends on another.
1471 /* Determine required relkind */
1472 switch (drop
->removeType
)
1475 relkind
= RELKIND_RELATION
;
1479 relkind
= RELKIND_INDEX
;
1482 case OBJECT_SEQUENCE
:
1483 relkind
= RELKIND_SEQUENCE
;
1487 relkind
= RELKIND_VIEW
;
1490 case OBJECT_MATVIEW
:
1491 relkind
= RELKIND_MATVIEW
;
1494 case OBJECT_FOREIGN_TABLE
:
1495 relkind
= RELKIND_FOREIGN_TABLE
;
1499 elog(ERROR
, "unrecognized drop object type: %d",
1500 (int) drop
->removeType
);
1501 relkind
= 0; /* keep compiler quiet */
1505 /* Lock and validate each relation; build a list of object addresses */
1506 objects
= new_object_addresses();
1508 foreach(cell
, drop
->objects
)
1510 RangeVar
*rel
= makeRangeVarFromNameList((List
*) lfirst(cell
));
1513 struct DropRelationCallbackState state
;
1516 * These next few steps are a great deal like relation_openrv, but we
1517 * don't bother building a relcache entry since we don't need it.
1519 * Check for shared-cache-inval messages before trying to access the
1520 * relation. This is needed to cover the case where the name
1521 * identifies a rel that has been dropped and recreated since the
1522 * start of our transaction: if we don't flush the old syscache entry,
1523 * then we'll latch onto that entry and suffer an error later.
1525 AcceptInvalidationMessages();
1527 /* Look up the appropriate relation using namespace search. */
1528 state
.expected_relkind
= relkind
;
1529 state
.heap_lockmode
= drop
->concurrent
?
1530 ShareUpdateExclusiveLock
: AccessExclusiveLock
;
1531 /* We must initialize these fields to show that no locks are held: */
1532 state
.heapOid
= InvalidOid
;
1533 state
.partParentOid
= InvalidOid
;
1535 relOid
= RangeVarGetRelidExtended(rel
, lockmode
, RVR_MISSING_OK
,
1536 RangeVarCallbackForDropRelation
,
1540 if (!OidIsValid(relOid
))
1542 DropErrorMsgNonExistent(rel
, relkind
, drop
->missing_ok
);
1547 * Decide if concurrent mode needs to be used here or not. The
1548 * callback retrieved the rel's persistence for us.
1550 if (drop
->concurrent
&&
1551 state
.actual_relpersistence
!= RELPERSISTENCE_TEMP
)
1553 Assert(list_length(drop
->objects
) == 1 &&
1554 drop
->removeType
== OBJECT_INDEX
);
1555 flags
|= PERFORM_DELETION_CONCURRENTLY
;
1559 * Concurrent index drop cannot be used with partitioned indexes,
1562 if ((flags
& PERFORM_DELETION_CONCURRENTLY
) != 0 &&
1563 state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1565 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1566 errmsg("cannot drop partitioned index \"%s\" concurrently",
1570 * If we're told to drop a partitioned index, we must acquire lock on
1571 * all the children of its parent partitioned table before proceeding.
1572 * Otherwise we'd try to lock the child index partitions before their
1573 * tables, leading to potential deadlock against other sessions that
1574 * will lock those objects in the other order.
1576 if (state
.actual_relkind
== RELKIND_PARTITIONED_INDEX
)
1577 (void) find_all_inheritors(state
.heapOid
,
1578 state
.heap_lockmode
,
1581 /* OK, we're ready to delete this one */
1582 obj
.classId
= RelationRelationId
;
1583 obj
.objectId
= relOid
;
1584 obj
.objectSubId
= 0;
1586 add_exact_object_address(&obj
, objects
);
1589 performMultipleDeletions(objects
, drop
->behavior
, flags
);
1591 free_object_addresses(objects
);
1595 * Before acquiring a table lock, check whether we have sufficient rights.
1596 * In the case of DROP INDEX, also try to lock the table before the index.
1597 * Also, if the table to be dropped is a partition, we try to lock the parent
1601 RangeVarCallbackForDropRelation(const RangeVar
*rel
, Oid relOid
, Oid oldRelOid
,
1605 struct DropRelationCallbackState
*state
;
1606 char expected_relkind
;
1608 Form_pg_class classform
;
1609 LOCKMODE heap_lockmode
;
1610 bool invalid_system_index
= false;
1612 state
= (struct DropRelationCallbackState
*) arg
;
1613 heap_lockmode
= state
->heap_lockmode
;
1616 * If we previously locked some other index's heap, and the name we're
1617 * looking up no longer refers to that relation, release the now-useless
1620 if (relOid
!= oldRelOid
&& OidIsValid(state
->heapOid
))
1622 UnlockRelationOid(state
->heapOid
, heap_lockmode
);
1623 state
->heapOid
= InvalidOid
;
1627 * Similarly, if we previously locked some other partition's heap, and the
1628 * name we're looking up no longer refers to that relation, release the
1631 if (relOid
!= oldRelOid
&& OidIsValid(state
->partParentOid
))
1633 UnlockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1634 state
->partParentOid
= InvalidOid
;
1637 /* Didn't find a relation, so no need for locking or permission checks. */
1638 if (!OidIsValid(relOid
))
1641 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
1642 if (!HeapTupleIsValid(tuple
))
1643 return; /* concurrently dropped, so nothing to do */
1644 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
1645 is_partition
= classform
->relispartition
;
1647 /* Pass back some data to save lookups in RemoveRelations */
1648 state
->actual_relkind
= classform
->relkind
;
1649 state
->actual_relpersistence
= classform
->relpersistence
;
1652 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1653 * but RemoveRelations() can only pass one relkind for a given relation.
1654 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1655 * That means we must be careful before giving the wrong type error when
1656 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1657 * exists with indexes.
1659 if (classform
->relkind
== RELKIND_PARTITIONED_TABLE
)
1660 expected_relkind
= RELKIND_RELATION
;
1661 else if (classform
->relkind
== RELKIND_PARTITIONED_INDEX
)
1662 expected_relkind
= RELKIND_INDEX
;
1664 expected_relkind
= classform
->relkind
;
1666 if (state
->expected_relkind
!= expected_relkind
)
1667 DropErrorMsgWrongType(rel
->relname
, classform
->relkind
,
1668 state
->expected_relkind
);
1670 /* Allow DROP to either table owner or schema owner */
1671 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()) &&
1672 !object_ownercheck(NamespaceRelationId
, classform
->relnamespace
, GetUserId()))
1673 aclcheck_error(ACLCHECK_NOT_OWNER
,
1674 get_relkind_objtype(classform
->relkind
),
1678 * Check the case of a system index that might have been invalidated by a
1679 * failed concurrent process and allow its drop. For the time being, this
1680 * only concerns indexes of toast relations that became invalid during a
1681 * REINDEX CONCURRENTLY process.
1683 if (IsSystemClass(relOid
, classform
) && classform
->relkind
== RELKIND_INDEX
)
1686 Form_pg_index indexform
;
1689 locTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(relOid
));
1690 if (!HeapTupleIsValid(locTuple
))
1692 ReleaseSysCache(tuple
);
1696 indexform
= (Form_pg_index
) GETSTRUCT(locTuple
);
1697 indisvalid
= indexform
->indisvalid
;
1698 ReleaseSysCache(locTuple
);
1700 /* Mark object as being an invalid index of system catalogs */
1702 invalid_system_index
= true;
1705 /* In the case of an invalid index, it is fine to bypass this check */
1706 if (!invalid_system_index
&& !allowSystemTableMods
&& IsSystemClass(relOid
, classform
))
1708 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1709 errmsg("permission denied: \"%s\" is a system catalog",
1712 ReleaseSysCache(tuple
);
1715 * In DROP INDEX, attempt to acquire lock on the parent table before
1716 * locking the index. index_drop() will need this anyway, and since
1717 * regular queries lock tables before their indexes, we risk deadlock if
1718 * we do it the other way around. No error if we don't find a pg_index
1719 * entry, though --- the relation may have been dropped. Note that this
1720 * code will execute for either plain or partitioned indexes.
1722 if (expected_relkind
== RELKIND_INDEX
&&
1723 relOid
!= oldRelOid
)
1725 state
->heapOid
= IndexGetRelation(relOid
, true);
1726 if (OidIsValid(state
->heapOid
))
1727 LockRelationOid(state
->heapOid
, heap_lockmode
);
1731 * Similarly, if the relation is a partition, we must acquire lock on its
1732 * parent before locking the partition. That's because queries lock the
1733 * parent before its partitions, so we risk deadlock if we do it the other
1736 if (is_partition
&& relOid
!= oldRelOid
)
1738 state
->partParentOid
= get_partition_parent(relOid
, true);
1739 if (OidIsValid(state
->partParentOid
))
1740 LockRelationOid(state
->partParentOid
, AccessExclusiveLock
);
1746 * Executes a TRUNCATE command.
1748 * This is a multi-relation truncate. We first open and grab exclusive
1749 * lock on all relations involved, checking permissions and otherwise
1750 * verifying that the relation is OK for truncation. Note that if relations
1751 * are foreign tables, at this stage, we have not yet checked that their
1752 * foreign data in external data sources are OK for truncation. These are
1753 * checked when foreign data are actually truncated later. In CASCADE mode,
1754 * relations having FK references to the targeted relations are automatically
1755 * added to the group; in RESTRICT mode, we check that all FK references are
1756 * internal to the group that's being truncated. Finally all the relations
1757 * are truncated and reindexed.
1760 ExecuteTruncate(TruncateStmt
*stmt
)
1764 List
*relids_logged
= NIL
;
1768 * Open, exclusive-lock, and check all the explicitly-specified relations
1770 foreach(cell
, stmt
->relations
)
1772 RangeVar
*rv
= lfirst(cell
);
1774 bool recurse
= rv
->inh
;
1776 LOCKMODE lockmode
= AccessExclusiveLock
;
1778 myrelid
= RangeVarGetRelidExtended(rv
, lockmode
,
1779 0, RangeVarCallbackForTruncate
,
1782 /* don't throw error for "TRUNCATE foo, foo" */
1783 if (list_member_oid(relids
, myrelid
))
1786 /* open the relation, we already hold a lock on it */
1787 rel
= table_open(myrelid
, NoLock
);
1790 * RangeVarGetRelidExtended() has done most checks with its callback,
1791 * but other checks with the now-opened Relation remain.
1793 truncate_check_activity(rel
);
1795 rels
= lappend(rels
, rel
);
1796 relids
= lappend_oid(relids
, myrelid
);
1798 /* Log this relation only if needed for logical decoding */
1799 if (RelationIsLogicallyLogged(rel
))
1800 relids_logged
= lappend_oid(relids_logged
, myrelid
);
1807 children
= find_all_inheritors(myrelid
, lockmode
, NULL
);
1809 foreach(child
, children
)
1811 Oid childrelid
= lfirst_oid(child
);
1813 if (list_member_oid(relids
, childrelid
))
1816 /* find_all_inheritors already got lock */
1817 rel
= table_open(childrelid
, NoLock
);
1820 * It is possible that the parent table has children that are
1821 * temp tables of other backends. We cannot safely access
1822 * such tables (because of buffering issues), and the best
1823 * thing to do is to silently ignore them. Note that this
1824 * check is the same as one of the checks done in
1825 * truncate_check_activity() called below, still it is kept
1826 * here for simplicity.
1828 if (RELATION_IS_OTHER_TEMP(rel
))
1830 table_close(rel
, lockmode
);
1835 * Inherited TRUNCATE commands perform access permission
1836 * checks on the parent table only. So we skip checking the
1837 * children's permissions and don't call
1838 * truncate_check_perms() here.
1840 truncate_check_rel(RelationGetRelid(rel
), rel
->rd_rel
);
1841 truncate_check_activity(rel
);
1843 rels
= lappend(rels
, rel
);
1844 relids
= lappend_oid(relids
, childrelid
);
1846 /* Log this relation only if needed for logical decoding */
1847 if (RelationIsLogicallyLogged(rel
))
1848 relids_logged
= lappend_oid(relids_logged
, childrelid
);
1851 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
1853 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
1854 errmsg("cannot truncate only a partitioned table"),
1855 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1858 ExecuteTruncateGuts(rels
, relids
, relids_logged
,
1859 stmt
->behavior
, stmt
->restart_seqs
, false);
1861 /* And close the rels */
1864 Relation rel
= (Relation
) lfirst(cell
);
1866 table_close(rel
, NoLock
);
1871 * ExecuteTruncateGuts
1873 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1874 * command (see above) as well as replication subscribers that execute a
1875 * replicated TRUNCATE action.
1877 * explicit_rels is the list of Relations to truncate that the command
1878 * specified. relids is the list of Oids corresponding to explicit_rels.
1879 * relids_logged is the list of Oids (a subset of relids) that require
1880 * WAL-logging. This is all a bit redundant, but the existing callers have
1881 * this information handy in this form.
1884 ExecuteTruncateGuts(List
*explicit_rels
,
1886 List
*relids_logged
,
1887 DropBehavior behavior
, bool restart_seqs
,
1888 bool run_as_table_owner
)
1891 List
*seq_relids
= NIL
;
1892 HTAB
*ft_htab
= NULL
;
1894 ResultRelInfo
*resultRelInfos
;
1895 ResultRelInfo
*resultRelInfo
;
1896 SubTransactionId mySubid
;
1901 * Check the explicitly-specified relations.
1903 * In CASCADE mode, suck in all referencing relations as well. This
1904 * requires multiple iterations to find indirectly-dependent relations. At
1905 * each phase, we need to exclusive-lock new rels before looking for their
1906 * dependencies, else we might miss something. Also, we check each rel as
1907 * soon as we open it, to avoid a faux pas such as holding lock for a long
1908 * time on a rel we have no permissions for.
1910 rels
= list_copy(explicit_rels
);
1911 if (behavior
== DROP_CASCADE
)
1917 newrelids
= heap_truncate_find_FKs(relids
);
1918 if (newrelids
== NIL
)
1919 break; /* nothing else to add */
1921 foreach(cell
, newrelids
)
1923 Oid relid
= lfirst_oid(cell
);
1926 rel
= table_open(relid
, AccessExclusiveLock
);
1928 (errmsg("truncate cascades to table \"%s\"",
1929 RelationGetRelationName(rel
))));
1930 truncate_check_rel(relid
, rel
->rd_rel
);
1931 truncate_check_perms(relid
, rel
->rd_rel
);
1932 truncate_check_activity(rel
);
1933 rels
= lappend(rels
, rel
);
1934 relids
= lappend_oid(relids
, relid
);
1936 /* Log this relation only if needed for logical decoding */
1937 if (RelationIsLogicallyLogged(rel
))
1938 relids_logged
= lappend_oid(relids_logged
, relid
);
1944 * Check foreign key references. In CASCADE mode, this should be
1945 * unnecessary since we just pulled in all the references; but as a
1946 * cross-check, do it anyway if in an Assert-enabled build.
1948 #ifdef USE_ASSERT_CHECKING
1949 heap_truncate_check_FKs(rels
, false);
1951 if (behavior
== DROP_RESTRICT
)
1952 heap_truncate_check_FKs(rels
, false);
1956 * If we are asked to restart sequences, find all the sequences, lock them
1957 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1958 * We want to do this early since it's pointless to do all the truncation
1959 * work only to fail on sequence permissions.
1965 Relation rel
= (Relation
) lfirst(cell
);
1966 List
*seqlist
= getOwnedSequences(RelationGetRelid(rel
));
1969 foreach(seqcell
, seqlist
)
1971 Oid seq_relid
= lfirst_oid(seqcell
);
1974 seq_rel
= relation_open(seq_relid
, AccessExclusiveLock
);
1976 /* This check must match AlterSequence! */
1977 if (!object_ownercheck(RelationRelationId
, seq_relid
, GetUserId()))
1978 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_SEQUENCE
,
1979 RelationGetRelationName(seq_rel
));
1981 seq_relids
= lappend_oid(seq_relids
, seq_relid
);
1983 relation_close(seq_rel
, NoLock
);
1988 /* Prepare to catch AFTER triggers. */
1989 AfterTriggerBeginQuery();
1992 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1993 * each relation. We don't need to call ExecOpenIndices, though.
1995 * We put the ResultRelInfos in the es_opened_result_relations list, even
1996 * though we don't have a range table and don't populate the
1997 * es_result_relations array. That's a bit bogus, but it's enough to make
1998 * ExecGetTriggerResultRel() find them.
2000 estate
= CreateExecutorState();
2001 resultRelInfos
= (ResultRelInfo
*)
2002 palloc(list_length(rels
) * sizeof(ResultRelInfo
));
2003 resultRelInfo
= resultRelInfos
;
2006 Relation rel
= (Relation
) lfirst(cell
);
2008 InitResultRelInfo(resultRelInfo
,
2010 0, /* dummy rangetable index */
2013 estate
->es_opened_result_relations
=
2014 lappend(estate
->es_opened_result_relations
, resultRelInfo
);
2019 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2020 * truncating (this is because one of them might throw an error). Also, if
2021 * we were to allow them to prevent statement execution, that would need
2022 * to be handled here.
2024 resultRelInfo
= resultRelInfos
;
2029 if (run_as_table_owner
)
2030 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2032 ExecBSTruncateTriggers(estate
, resultRelInfo
);
2033 if (run_as_table_owner
)
2034 RestoreUserContext(&ucxt
);
2039 * OK, truncate each table.
2041 mySubid
= GetCurrentSubTransactionId();
2045 Relation rel
= (Relation
) lfirst(cell
);
2047 /* Skip partitioned tables as there is nothing to do */
2048 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
2052 * Build the lists of foreign tables belonging to each foreign server
2053 * and pass each list to the foreign data wrapper's callback function,
2054 * so that each server can truncate its all foreign tables in bulk.
2055 * Each list is saved as a single entry in a hash table that uses the
2056 * server OID as lookup key.
2058 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
2060 Oid serverid
= GetForeignServerIdByRelId(RelationGetRelid(rel
));
2062 ForeignTruncateInfo
*ft_info
;
2064 /* First time through, initialize hashtable for foreign tables */
2069 memset(&hctl
, 0, sizeof(HASHCTL
));
2070 hctl
.keysize
= sizeof(Oid
);
2071 hctl
.entrysize
= sizeof(ForeignTruncateInfo
);
2072 hctl
.hcxt
= CurrentMemoryContext
;
2074 ft_htab
= hash_create("TRUNCATE for Foreign Tables",
2075 32, /* start small and extend */
2077 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
2080 /* Find or create cached entry for the foreign table */
2081 ft_info
= hash_search(ft_htab
, &serverid
, HASH_ENTER
, &found
);
2083 ft_info
->rels
= NIL
;
2086 * Save the foreign table in the entry of the server that the
2087 * foreign table belongs to.
2089 ft_info
->rels
= lappend(ft_info
->rels
, rel
);
2094 * Normally, we need a transaction-safe truncation here. However, if
2095 * the table was either created in the current (sub)transaction or has
2096 * a new relfilenumber in the current (sub)transaction, then we can
2097 * just truncate it in-place, because a rollback would cause the whole
2098 * table or the current physical file to be thrown away anyway.
2100 if (rel
->rd_createSubid
== mySubid
||
2101 rel
->rd_newRelfilelocatorSubid
== mySubid
)
2103 /* Immediate, non-rollbackable truncation is OK */
2104 heap_truncate_one_rel(rel
);
2110 ReindexParams reindex_params
= {0};
2113 * This effectively deletes all rows in the table, and may be done
2114 * in a serializable transaction. In that case we must record a
2115 * rw-conflict in to this transaction from each transaction
2116 * holding a predicate lock on the table.
2118 CheckTableForSerializableConflictIn(rel
);
2121 * Need the full transaction-safe pushups.
2123 * Create a new empty storage file for the relation, and assign it
2124 * as the relfilenumber value. The old storage file is scheduled
2125 * for deletion at commit.
2127 RelationSetNewRelfilenumber(rel
, rel
->rd_rel
->relpersistence
);
2129 heap_relid
= RelationGetRelid(rel
);
2132 * The same for the toast table, if any.
2134 toast_relid
= rel
->rd_rel
->reltoastrelid
;
2135 if (OidIsValid(toast_relid
))
2137 Relation toastrel
= relation_open(toast_relid
,
2138 AccessExclusiveLock
);
2140 RelationSetNewRelfilenumber(toastrel
,
2141 toastrel
->rd_rel
->relpersistence
);
2142 table_close(toastrel
, NoLock
);
2146 * Reconstruct the indexes to match, and we're done.
2148 reindex_relation(NULL
, heap_relid
, REINDEX_REL_PROCESS_TOAST
,
2152 pgstat_count_truncate(rel
);
2155 /* Now go through the hash table, and truncate foreign tables */
2158 ForeignTruncateInfo
*ft_info
;
2159 HASH_SEQ_STATUS seq
;
2161 hash_seq_init(&seq
, ft_htab
);
2165 while ((ft_info
= hash_seq_search(&seq
)) != NULL
)
2167 FdwRoutine
*routine
= GetFdwRoutineByServerId(ft_info
->serverid
);
2169 /* truncate_check_rel() has checked that already */
2170 Assert(routine
->ExecForeignTruncate
!= NULL
);
2172 routine
->ExecForeignTruncate(ft_info
->rels
,
2179 hash_destroy(ft_htab
);
2185 * Restart owned sequences if we were asked to.
2187 foreach(cell
, seq_relids
)
2189 Oid seq_relid
= lfirst_oid(cell
);
2191 ResetSequence(seq_relid
);
2195 * Write a WAL record to allow this set of actions to be logically
2198 * Assemble an array of relids so we can write a single WAL record for the
2201 if (relids_logged
!= NIL
)
2203 xl_heap_truncate xlrec
;
2206 /* should only get here if wal_level >= logical */
2207 Assert(XLogLogicalInfoActive());
2209 logrelids
= palloc(list_length(relids_logged
) * sizeof(Oid
));
2210 foreach(cell
, relids_logged
)
2211 logrelids
[i
++] = lfirst_oid(cell
);
2213 xlrec
.dbId
= MyDatabaseId
;
2214 xlrec
.nrelids
= list_length(relids_logged
);
2216 if (behavior
== DROP_CASCADE
)
2217 xlrec
.flags
|= XLH_TRUNCATE_CASCADE
;
2219 xlrec
.flags
|= XLH_TRUNCATE_RESTART_SEQS
;
2222 XLogRegisterData((char *) &xlrec
, SizeOfHeapTruncate
);
2223 XLogRegisterData((char *) logrelids
, list_length(relids_logged
) * sizeof(Oid
));
2225 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN
);
2227 (void) XLogInsert(RM_HEAP_ID
, XLOG_HEAP_TRUNCATE
);
2231 * Process all AFTER STATEMENT TRUNCATE triggers.
2233 resultRelInfo
= resultRelInfos
;
2238 if (run_as_table_owner
)
2239 SwitchToUntrustedUser(resultRelInfo
->ri_RelationDesc
->rd_rel
->relowner
,
2241 ExecASTruncateTriggers(estate
, resultRelInfo
);
2242 if (run_as_table_owner
)
2243 RestoreUserContext(&ucxt
);
2247 /* Handle queued AFTER triggers */
2248 AfterTriggerEndQuery(estate
);
2250 /* We can clean up the EState now */
2251 FreeExecutorState(estate
);
2254 * Close any rels opened by CASCADE (can't do this while EState still
2257 rels
= list_difference_ptr(rels
, explicit_rels
);
2260 Relation rel
= (Relation
) lfirst(cell
);
2262 table_close(rel
, NoLock
);
2267 * Check that a given relation is safe to truncate. Subroutine for
2268 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2271 truncate_check_rel(Oid relid
, Form_pg_class reltuple
)
2273 char *relname
= NameStr(reltuple
->relname
);
2276 * Only allow truncate on regular tables, foreign tables using foreign
2277 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2278 * latter are only being included here for the following checks; no
2279 * physical truncation will occur in their case.).
2281 if (reltuple
->relkind
== RELKIND_FOREIGN_TABLE
)
2283 Oid serverid
= GetForeignServerIdByRelId(relid
);
2284 FdwRoutine
*fdwroutine
= GetFdwRoutineByServerId(serverid
);
2286 if (!fdwroutine
->ExecForeignTruncate
)
2288 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2289 errmsg("cannot truncate foreign table \"%s\"",
2292 else if (reltuple
->relkind
!= RELKIND_RELATION
&&
2293 reltuple
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2295 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2296 errmsg("\"%s\" is not a table", relname
)));
2299 * Most system catalogs can't be truncated at all, or at least not unless
2300 * allow_system_table_mods=on. As an exception, however, we allow
2301 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2302 * to change its relfilenode to match the old cluster, and allowing a
2303 * TRUNCATE command to be executed is the easiest way of doing that.
2305 if (!allowSystemTableMods
&& IsSystemClass(relid
, reltuple
)
2306 && (!IsBinaryUpgrade
|| relid
!= LargeObjectRelationId
))
2308 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
2309 errmsg("permission denied: \"%s\" is a system catalog",
2312 InvokeObjectTruncateHook(relid
);
2316 * Check that current user has the permission to truncate given relation.
2319 truncate_check_perms(Oid relid
, Form_pg_class reltuple
)
2321 char *relname
= NameStr(reltuple
->relname
);
2322 AclResult aclresult
;
2324 /* Permissions checks */
2325 aclresult
= pg_class_aclcheck(relid
, GetUserId(), ACL_TRUNCATE
);
2326 if (aclresult
!= ACLCHECK_OK
)
2327 aclcheck_error(aclresult
, get_relkind_objtype(reltuple
->relkind
),
2332 * Set of extra sanity checks to check if a given relation is safe to
2333 * truncate. This is split with truncate_check_rel() as
2334 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2337 truncate_check_activity(Relation rel
)
2340 * Don't allow truncate on temp tables of other backends ... their local
2341 * buffer manager is not going to cope.
2343 if (RELATION_IS_OTHER_TEMP(rel
))
2345 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2346 errmsg("cannot truncate temporary tables of other sessions")));
2349 * Also check for active uses of the relation in the current transaction,
2350 * including open scans and pending AFTER trigger events.
2352 CheckTableNotInUse(rel
, "TRUNCATE");
2357 * returns the name corresponding to a typstorage/attstorage enum value
2360 storage_name(char c
)
2364 case TYPSTORAGE_PLAIN
:
2366 case TYPSTORAGE_EXTERNAL
:
2368 case TYPSTORAGE_EXTENDED
:
2370 case TYPSTORAGE_MAIN
:
2379 * Returns new schema given initial schema and superclasses.
2382 * 'columns' is the column/attribute definition for the table. (It's a list
2383 * of ColumnDef's.) It is destructively changed.
2384 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2385 * 'relpersistence' is the persistence type of the table.
2386 * 'is_partition' tells if the table is a partition.
2389 * 'supconstr' receives a list of constraints belonging to the parents,
2390 * updated as necessary to be valid for the child.
2393 * Completed schema list.
2396 * The order in which the attributes are inherited is very important.
2397 * Intuitively, the inherited attributes should come first. If a table
2398 * inherits from multiple parents, the order of those attributes are
2399 * according to the order of the parents specified in CREATE TABLE.
2401 * Here's an example:
2403 * create table person (name text, age int4, location point);
2404 * create table emp (salary int4, manager text) inherits(person);
2405 * create table student (gpa float8) inherits (person);
2406 * create table stud_emp (percent int4) inherits (emp, student);
2408 * The order of the attributes of stud_emp is:
2410 * person {1:name, 2:age, 3:location}
2412 * {6:gpa} student emp {4:salary, 5:manager}
2414 * stud_emp {7:percent}
2416 * If the same attribute name appears multiple times, then it appears
2417 * in the result table in the proper location for its first appearance.
2419 * Constraints (including not-null constraints) for the child table
2420 * are the union of all relevant constraints, from both the child schema
2421 * and parent tables.
2423 * The default value for a child column is defined as:
2424 * (1) If the child schema specifies a default, that value is used.
2425 * (2) If neither the child nor any parent specifies a default, then
2426 * the column will not have a default.
2427 * (3) If conflicting defaults are inherited from different parents
2428 * (and not overridden by the child), an error is raised.
2429 * (4) Otherwise the inherited default is used.
2431 * Note that the default-value infrastructure is used for generated
2432 * columns' expressions too, so most of the preceding paragraph applies
2433 * to generation expressions too. We insist that a child column be
2434 * generated if and only if its parent(s) are, but it need not have
2435 * the same generation expression.
2439 MergeAttributes(List
*columns
, const List
*supers
, char relpersistence
,
2440 bool is_partition
, List
**supconstr
)
2442 List
*inh_columns
= NIL
;
2443 List
*constraints
= NIL
;
2444 bool have_bogus_defaults
= false;
2446 static Node bogus_marker
= {0}; /* marks conflicting defaults */
2447 List
*saved_columns
= NIL
;
2451 * Check for and reject tables with too many columns. We perform this
2452 * check relatively early for two reasons: (a) we don't run the risk of
2453 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2454 * okay if we're processing <= 1600 columns, but could take minutes to
2455 * execute if the user attempts to create a table with hundreds of
2456 * thousands of columns.
2458 * Note that we also need to check that we do not exceed this figure after
2459 * including columns from inherited relations.
2461 if (list_length(columns
) > MaxHeapAttributeNumber
)
2463 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2464 errmsg("tables can have at most %d columns",
2465 MaxHeapAttributeNumber
)));
2468 * Check for duplicate names in the explicit list of attributes.
2470 * Although we might consider merging such entries in the same way that we
2471 * handle name conflicts for inherited attributes, it seems to make more
2472 * sense to assume such conflicts are errors.
2474 * We don't use foreach() here because we have two nested loops over the
2475 * columns list, with possible element deletions in the inner one. If we
2476 * used foreach_delete_current() it could only fix up the state of one of
2477 * the loops, so it seems cleaner to use looping over list indexes for
2478 * both loops. Note that any deletion will happen beyond where the outer
2479 * loop is, so its index never needs adjustment.
2481 for (int coldefpos
= 0; coldefpos
< list_length(columns
); coldefpos
++)
2483 ColumnDef
*coldef
= list_nth_node(ColumnDef
, columns
, coldefpos
);
2485 if (!is_partition
&& coldef
->typeName
== NULL
)
2488 * Typed table column option that does not belong to a column from
2489 * the type. This works because the columns from the type come
2490 * first in the list. (We omit this check for partition column
2491 * lists; those are processed separately below.)
2494 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2495 errmsg("column \"%s\" does not exist",
2499 /* restpos scans all entries beyond coldef; incr is in loop body */
2500 for (int restpos
= coldefpos
+ 1; restpos
< list_length(columns
);)
2502 ColumnDef
*restdef
= list_nth_node(ColumnDef
, columns
, restpos
);
2504 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2506 if (coldef
->is_from_type
)
2509 * merge the column options into the column from the type
2511 coldef
->is_not_null
= restdef
->is_not_null
;
2512 coldef
->raw_default
= restdef
->raw_default
;
2513 coldef
->cooked_default
= restdef
->cooked_default
;
2514 coldef
->constraints
= restdef
->constraints
;
2515 coldef
->is_from_type
= false;
2516 columns
= list_delete_nth_cell(columns
, restpos
);
2520 (errcode(ERRCODE_DUPLICATE_COLUMN
),
2521 errmsg("column \"%s\" specified more than once",
2530 * In case of a partition, there are no new column definitions, only dummy
2531 * ColumnDefs created for column constraints. Set them aside for now and
2532 * process them at the end.
2536 saved_columns
= columns
;
2541 * Scan the parents left-to-right, and merge their attributes to form a
2542 * list of inherited columns (inh_columns).
2547 Oid parent
= lfirst_oid(lc
);
2549 TupleDesc tupleDesc
;
2550 TupleConstr
*constr
;
2552 List
*inherited_defaults
;
2553 List
*cols_with_defaults
;
2557 /* caller already got lock */
2558 relation
= table_open(parent
, NoLock
);
2561 * Check for active uses of the parent partitioned table in the
2562 * current transaction, such as being used in some manner by an
2563 * enclosing command.
2566 CheckTableNotInUse(relation
, "CREATE TABLE .. PARTITION OF");
2569 * We do not allow partitioned tables and partitions to participate in
2570 * regular inheritance.
2572 if (relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !is_partition
)
2574 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2575 errmsg("cannot inherit from partitioned table \"%s\"",
2576 RelationGetRelationName(relation
))));
2577 if (relation
->rd_rel
->relispartition
&& !is_partition
)
2579 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2580 errmsg("cannot inherit from partition \"%s\"",
2581 RelationGetRelationName(relation
))));
2583 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
2584 relation
->rd_rel
->relkind
!= RELKIND_FOREIGN_TABLE
&&
2585 relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
2587 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2588 errmsg("inherited relation \"%s\" is not a table or foreign table",
2589 RelationGetRelationName(relation
))));
2592 * If the parent is permanent, so must be all of its partitions. Note
2593 * that inheritance allows that case.
2596 relation
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
2597 relpersistence
== RELPERSISTENCE_TEMP
)
2599 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2600 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2601 RelationGetRelationName(relation
))));
2603 /* Permanent rels cannot inherit from temporary ones */
2604 if (relpersistence
!= RELPERSISTENCE_TEMP
&&
2605 relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
2607 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2608 errmsg(!is_partition
2609 ? "cannot inherit from temporary relation \"%s\""
2610 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2611 RelationGetRelationName(relation
))));
2613 /* If existing rel is temp, it must belong to this session */
2614 if (relation
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
2615 !relation
->rd_islocaltemp
)
2617 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
2618 errmsg(!is_partition
2619 ? "cannot inherit from temporary relation of another session"
2620 : "cannot create as partition of temporary relation of another session")));
2623 * We should have an UNDER permission flag for this, but for now,
2624 * demand that creator of a child table own the parent.
2626 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(relation
), GetUserId()))
2627 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(relation
->rd_rel
->relkind
),
2628 RelationGetRelationName(relation
));
2630 tupleDesc
= RelationGetDescr(relation
);
2631 constr
= tupleDesc
->constr
;
2634 * newattmap->attnums[] will contain the child-table attribute numbers
2635 * for the attributes of this parent table. (They are not the same
2636 * for parents after the first one, nor if we have dropped columns.)
2638 newattmap
= make_attrmap(tupleDesc
->natts
);
2640 /* We can't process inherited defaults until newattmap is complete. */
2641 inherited_defaults
= cols_with_defaults
= NIL
;
2643 for (AttrNumber parent_attno
= 1; parent_attno
<= tupleDesc
->natts
;
2646 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
,
2648 char *attributeName
= NameStr(attribute
->attname
);
2651 ColumnDef
*mergeddef
;
2654 * Ignore dropped columns in the parent.
2656 if (attribute
->attisdropped
)
2657 continue; /* leave newattmap->attnums entry as zero */
2660 * Create new column definition
2662 newdef
= makeColumnDef(attributeName
, attribute
->atttypid
,
2663 attribute
->atttypmod
, attribute
->attcollation
);
2664 newdef
->is_not_null
= attribute
->attnotnull
;
2665 newdef
->storage
= attribute
->attstorage
;
2666 newdef
->generated
= attribute
->attgenerated
;
2667 if (CompressionMethodIsValid(attribute
->attcompression
))
2668 newdef
->compression
=
2669 pstrdup(GetCompressionMethodName(attribute
->attcompression
));
2672 * Regular inheritance children are independent enough not to
2673 * inherit identity columns. But partitions are integral part of
2674 * a partitioned table and inherit identity column.
2677 newdef
->identity
= attribute
->attidentity
;
2680 * Does it match some previously considered column from another
2683 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2684 if (exist_attno
> 0)
2687 * Yes, try to merge the two column definitions.
2689 mergeddef
= MergeInheritedAttribute(inh_columns
, exist_attno
, newdef
);
2691 newattmap
->attnums
[parent_attno
- 1] = exist_attno
;
2694 * Partitions have only one parent, so conflict should never
2697 Assert(!is_partition
);
2702 * No, create a new inherited column
2704 newdef
->inhcount
= 1;
2705 newdef
->is_local
= false;
2706 inh_columns
= lappend(inh_columns
, newdef
);
2708 newattmap
->attnums
[parent_attno
- 1] = ++child_attno
;
2713 * Locate default/generation expression if any
2715 if (attribute
->atthasdef
)
2719 this_default
= TupleDescGetDefault(tupleDesc
, parent_attno
);
2720 if (this_default
== NULL
)
2721 elog(ERROR
, "default expression not found for attribute %d of relation \"%s\"",
2722 parent_attno
, RelationGetRelationName(relation
));
2725 * If it's a GENERATED default, it might contain Vars that
2726 * need to be mapped to the inherited column(s)' new numbers.
2727 * We can't do that till newattmap is ready, so just remember
2728 * all the inherited default expressions for the moment.
2730 inherited_defaults
= lappend(inherited_defaults
, this_default
);
2731 cols_with_defaults
= lappend(cols_with_defaults
, mergeddef
);
2736 * Now process any inherited default expressions, adjusting attnos
2737 * using the completed newattmap map.
2739 forboth(lc1
, inherited_defaults
, lc2
, cols_with_defaults
)
2741 Node
*this_default
= (Node
*) lfirst(lc1
);
2742 ColumnDef
*def
= (ColumnDef
*) lfirst(lc2
);
2743 bool found_whole_row
;
2745 /* Adjust Vars to match new table's column numbering */
2746 this_default
= map_variable_attnos(this_default
,
2749 InvalidOid
, &found_whole_row
);
2752 * For the moment we have to reject whole-row variables. We could
2753 * convert them, if we knew the new table's rowtype OID, but that
2754 * hasn't been assigned yet. (A variable could only appear in a
2755 * generation expression, so the error message is correct.)
2757 if (found_whole_row
)
2759 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2760 errmsg("cannot convert whole-row table reference"),
2761 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2763 RelationGetRelationName(relation
))));
2766 * If we already had a default from some prior parent, check to
2767 * see if they are the same. If so, no problem; if not, mark the
2768 * column as having a bogus default. Below, we will complain if
2769 * the bogus default isn't overridden by the child columns.
2771 Assert(def
->raw_default
== NULL
);
2772 if (def
->cooked_default
== NULL
)
2773 def
->cooked_default
= this_default
;
2774 else if (!equal(def
->cooked_default
, this_default
))
2776 def
->cooked_default
= &bogus_marker
;
2777 have_bogus_defaults
= true;
2782 * Now copy the CHECK constraints of this parent, adjusting attnos
2783 * using the completed newattmap map. Identically named constraints
2784 * are merged if possible, else we throw error.
2786 if (constr
&& constr
->num_check
> 0)
2788 ConstrCheck
*check
= constr
->check
;
2790 for (int i
= 0; i
< constr
->num_check
; i
++)
2792 char *name
= check
[i
].ccname
;
2794 bool found_whole_row
;
2796 /* ignore if the constraint is non-inheritable */
2797 if (check
[i
].ccnoinherit
)
2800 /* Adjust Vars to match new table's column numbering */
2801 expr
= map_variable_attnos(stringToNode(check
[i
].ccbin
),
2804 InvalidOid
, &found_whole_row
);
2807 * For the moment we have to reject whole-row variables. We
2808 * could convert them, if we knew the new table's rowtype OID,
2809 * but that hasn't been assigned yet.
2811 if (found_whole_row
)
2813 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2814 errmsg("cannot convert whole-row table reference"),
2815 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2817 RelationGetRelationName(relation
))));
2819 constraints
= MergeCheckConstraint(constraints
, name
, expr
);
2823 free_attrmap(newattmap
);
2826 * Close the parent rel, but keep our lock on it until xact commit.
2827 * That will prevent someone else from deleting or ALTERing the parent
2828 * before the child is committed.
2830 table_close(relation
, NoLock
);
2834 * If we had no inherited attributes, the result columns are just the
2835 * explicitly declared columns. Otherwise, we need to merge the declared
2836 * columns into the inherited column list. Although, we never have any
2837 * explicitly declared columns if the table is a partition.
2839 if (inh_columns
!= NIL
)
2841 int newcol_attno
= 0;
2843 foreach(lc
, columns
)
2845 ColumnDef
*newdef
= lfirst_node(ColumnDef
, lc
);
2846 char *attributeName
= newdef
->colname
;
2850 * Partitions have only one parent and have no column definitions
2851 * of their own, so conflict should never occur.
2853 Assert(!is_partition
);
2858 * Does it match some inherited column?
2860 exist_attno
= findAttrByName(attributeName
, inh_columns
);
2861 if (exist_attno
> 0)
2864 * Yes, try to merge the two column definitions.
2866 MergeChildAttribute(inh_columns
, exist_attno
, newcol_attno
, newdef
);
2871 * No, attach new column unchanged to result columns.
2873 inh_columns
= lappend(inh_columns
, newdef
);
2877 columns
= inh_columns
;
2880 * Check that we haven't exceeded the legal # of columns after merging
2881 * in inherited columns.
2883 if (list_length(columns
) > MaxHeapAttributeNumber
)
2885 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
2886 errmsg("tables can have at most %d columns",
2887 MaxHeapAttributeNumber
)));
2891 * Now that we have the column definition list for a partition, we can
2892 * check whether the columns referenced in the column constraint specs
2893 * actually exist. Also, we merge parent's not-null constraints and
2894 * defaults into each corresponding column definition.
2898 foreach(lc
, saved_columns
)
2900 ColumnDef
*restdef
= lfirst(lc
);
2906 ColumnDef
*coldef
= lfirst(l
);
2908 if (strcmp(coldef
->colname
, restdef
->colname
) == 0)
2911 coldef
->is_not_null
|= restdef
->is_not_null
;
2914 * Check for conflicts related to generated columns.
2916 * Same rules as above: generated-ness has to match the
2917 * parent, but the contents of the generation expression
2920 if (coldef
->generated
)
2922 if (restdef
->raw_default
&& !restdef
->generated
)
2924 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2925 errmsg("column \"%s\" inherits from generated column but specifies default",
2926 restdef
->colname
)));
2927 if (restdef
->identity
)
2929 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2930 errmsg("column \"%s\" inherits from generated column but specifies identity",
2931 restdef
->colname
)));
2935 if (restdef
->generated
)
2937 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2938 errmsg("child column \"%s\" specifies generation expression",
2940 errhint("A child table column cannot be generated unless its parent column is.")));
2944 * Override the parent's default value for this column
2945 * (coldef->cooked_default) with the partition's local
2946 * definition (restdef->raw_default), if there's one. It
2947 * should be physically impossible to get a cooked default
2948 * in the local definition or a raw default in the
2949 * inherited definition, but make sure they're nulls, for
2952 Assert(restdef
->cooked_default
== NULL
);
2953 Assert(coldef
->raw_default
== NULL
);
2954 if (restdef
->raw_default
)
2956 coldef
->raw_default
= restdef
->raw_default
;
2957 coldef
->cooked_default
= NULL
;
2962 /* complain for constraints on columns not in parent */
2965 (errcode(ERRCODE_UNDEFINED_COLUMN
),
2966 errmsg("column \"%s\" does not exist",
2967 restdef
->colname
)));
2972 * If we found any conflicting parent default values, check to make sure
2973 * they were overridden by the child.
2975 if (have_bogus_defaults
)
2977 foreach(lc
, columns
)
2979 ColumnDef
*def
= lfirst(lc
);
2981 if (def
->cooked_default
== &bogus_marker
)
2985 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2986 errmsg("column \"%s\" inherits conflicting generation expressions",
2988 errhint("To resolve the conflict, specify a generation expression explicitly.")));
2991 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
2992 errmsg("column \"%s\" inherits conflicting default values",
2994 errhint("To resolve the conflict, specify a default explicitly.")));
2999 *supconstr
= constraints
;
3006 * MergeCheckConstraint
3007 * Try to merge an inherited CHECK constraint with previous ones
3009 * If we inherit identically-named constraints from multiple parents, we must
3010 * merge them, or throw an error if they don't have identical definitions.
3012 * constraints is a list of CookedConstraint structs for previous constraints.
3014 * If the new constraint matches an existing one, then the existing
3015 * constraint's inheritance count is updated. If there is a conflict (same
3016 * name but different expression), throw an error. If the constraint neither
3017 * matches nor conflicts with an existing one, a new constraint is appended to
3021 MergeCheckConstraint(List
*constraints
, const char *name
, Node
*expr
)
3024 CookedConstraint
*newcon
;
3026 foreach(lc
, constraints
)
3028 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lc
);
3030 Assert(ccon
->contype
== CONSTR_CHECK
);
3032 /* Non-matching names never conflict */
3033 if (strcmp(ccon
->name
, name
) != 0)
3036 if (equal(expr
, ccon
->expr
))
3038 /* OK to merge constraint with existing */
3040 if (ccon
->inhcount
< 0)
3042 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3043 errmsg("too many inheritance parents"));
3048 (errcode(ERRCODE_DUPLICATE_OBJECT
),
3049 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3054 * Constraint couldn't be merged with an existing one and also didn't
3055 * conflict with an existing one, so add it as a new one to the list.
3057 newcon
= palloc0_object(CookedConstraint
);
3058 newcon
->contype
= CONSTR_CHECK
;
3059 newcon
->name
= pstrdup(name
);
3060 newcon
->expr
= expr
;
3061 newcon
->inhcount
= 1;
3062 return lappend(constraints
, newcon
);
3066 * MergeChildAttribute
3067 * Merge given child attribute definition into given inherited attribute.
3070 * 'inh_columns' is the list of inherited ColumnDefs.
3071 * 'exist_attno' is the number of the inherited attribute in inh_columns
3072 * 'newcol_attno' is the attribute number in child table's schema definition
3073 * 'newdef' is the column/attribute definition from the child table.
3075 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3076 * ColumnDef remains unchanged.
3079 * - The attribute is merged according to the rules laid out in the prologue
3080 * of MergeAttributes().
3081 * - If matching inherited attribute exists but the child attribute can not be
3082 * merged into it, the function throws respective errors.
3083 * - A partition can not have its own column definitions. Hence this function
3084 * is applicable only to a regular inheritance child.
3087 MergeChildAttribute(List
*inh_columns
, int exist_attno
, int newcol_attno
, const ColumnDef
*newdef
)
3089 char *attributeName
= newdef
->colname
;
3098 if (exist_attno
== newcol_attno
)
3100 (errmsg("merging column \"%s\" with inherited definition",
3104 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName
),
3105 errdetail("User-specified column moved to the position of the inherited column.")));
3107 inhdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3110 * Must have the same type and typmod
3112 typenameTypeIdAndMod(NULL
, inhdef
->typeName
, &inhtypeid
, &inhtypmod
);
3113 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3114 if (inhtypeid
!= newtypeid
|| inhtypmod
!= newtypmod
)
3116 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3117 errmsg("column \"%s\" has a type conflict",
3119 errdetail("%s versus %s",
3120 format_type_with_typemod(inhtypeid
, inhtypmod
),
3121 format_type_with_typemod(newtypeid
, newtypmod
))));
3124 * Must have the same collation
3126 inhcollid
= GetColumnDefCollation(NULL
, inhdef
, inhtypeid
);
3127 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3128 if (inhcollid
!= newcollid
)
3130 (errcode(ERRCODE_COLLATION_MISMATCH
),
3131 errmsg("column \"%s\" has a collation conflict",
3133 errdetail("\"%s\" versus \"%s\"",
3134 get_collation_name(inhcollid
),
3135 get_collation_name(newcollid
))));
3138 * Identity is never inherited by a regular inheritance child. Pick
3139 * child's identity definition if there's one.
3141 inhdef
->identity
= newdef
->identity
;
3144 * Copy storage parameter
3146 if (inhdef
->storage
== 0)
3147 inhdef
->storage
= newdef
->storage
;
3148 else if (newdef
->storage
!= 0 && inhdef
->storage
!= newdef
->storage
)
3150 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3151 errmsg("column \"%s\" has a storage parameter conflict",
3153 errdetail("%s versus %s",
3154 storage_name(inhdef
->storage
),
3155 storage_name(newdef
->storage
))));
3158 * Copy compression parameter
3160 if (inhdef
->compression
== NULL
)
3161 inhdef
->compression
= newdef
->compression
;
3162 else if (newdef
->compression
!= NULL
)
3164 if (strcmp(inhdef
->compression
, newdef
->compression
) != 0)
3166 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3167 errmsg("column \"%s\" has a compression method conflict",
3169 errdetail("%s versus %s", inhdef
->compression
, newdef
->compression
)));
3173 * Merge of not-null constraints = OR 'em together
3175 inhdef
->is_not_null
|= newdef
->is_not_null
;
3178 * Check for conflicts related to generated columns.
3180 * If the parent column is generated, the child column will be made a
3181 * generated column if it isn't already. If it is a generated column,
3182 * we'll take its generation expression in preference to the parent's. We
3183 * must check that the child column doesn't specify a default value or
3184 * identity, which matches the rules for a single column in
3187 * Conversely, if the parent column is not generated, the child column
3188 * can't be either. (We used to allow that, but it results in being able
3189 * to override the generation expression via UPDATEs through the parent.)
3191 if (inhdef
->generated
)
3193 if (newdef
->raw_default
&& !newdef
->generated
)
3195 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3196 errmsg("column \"%s\" inherits from generated column but specifies default",
3198 if (newdef
->identity
)
3200 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3201 errmsg("column \"%s\" inherits from generated column but specifies identity",
3206 if (newdef
->generated
)
3208 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION
),
3209 errmsg("child column \"%s\" specifies generation expression",
3211 errhint("A child table column cannot be generated unless its parent column is.")));
3215 * If new def has a default, override previous default
3217 if (newdef
->raw_default
!= NULL
)
3219 inhdef
->raw_default
= newdef
->raw_default
;
3220 inhdef
->cooked_default
= newdef
->cooked_default
;
3223 /* Mark the column as locally defined */
3224 inhdef
->is_local
= true;
3228 * MergeInheritedAttribute
3229 * Merge given parent attribute definition into specified attribute
3230 * inherited from the previous parents.
3233 * 'inh_columns' is the list of previously inherited ColumnDefs.
3234 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3235 * 'newdef' is the new parent column/attribute definition to be merged.
3237 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3240 * - The attribute is merged according to the rules laid out in the prologue
3241 * of MergeAttributes().
3242 * - If matching inherited attribute exists but the new attribute can not be
3243 * merged into it, the function throws respective errors.
3244 * - A partition inherits from only a single parent. Hence this function is
3245 * applicable only to a regular inheritance.
3248 MergeInheritedAttribute(List
*inh_columns
,
3250 const ColumnDef
*newdef
)
3252 char *attributeName
= newdef
->colname
;
3262 (errmsg("merging multiple inherited definitions of column \"%s\"",
3264 prevdef
= list_nth_node(ColumnDef
, inh_columns
, exist_attno
- 1);
3267 * Must have the same type and typmod
3269 typenameTypeIdAndMod(NULL
, prevdef
->typeName
, &prevtypeid
, &prevtypmod
);
3270 typenameTypeIdAndMod(NULL
, newdef
->typeName
, &newtypeid
, &newtypmod
);
3271 if (prevtypeid
!= newtypeid
|| prevtypmod
!= newtypmod
)
3273 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3274 errmsg("inherited column \"%s\" has a type conflict",
3276 errdetail("%s versus %s",
3277 format_type_with_typemod(prevtypeid
, prevtypmod
),
3278 format_type_with_typemod(newtypeid
, newtypmod
))));
3281 * Merge of not-null constraints = OR 'em together
3283 prevdef
->is_not_null
|= newdef
->is_not_null
;
3286 * Must have the same collation
3288 prevcollid
= GetColumnDefCollation(NULL
, prevdef
, prevtypeid
);
3289 newcollid
= GetColumnDefCollation(NULL
, newdef
, newtypeid
);
3290 if (prevcollid
!= newcollid
)
3292 (errcode(ERRCODE_COLLATION_MISMATCH
),
3293 errmsg("inherited column \"%s\" has a collation conflict",
3295 errdetail("\"%s\" versus \"%s\"",
3296 get_collation_name(prevcollid
),
3297 get_collation_name(newcollid
))));
3300 * Copy/check storage parameter
3302 if (prevdef
->storage
== 0)
3303 prevdef
->storage
= newdef
->storage
;
3304 else if (prevdef
->storage
!= newdef
->storage
)
3306 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3307 errmsg("inherited column \"%s\" has a storage parameter conflict",
3309 errdetail("%s versus %s",
3310 storage_name(prevdef
->storage
),
3311 storage_name(newdef
->storage
))));
3314 * Copy/check compression parameter
3316 if (prevdef
->compression
== NULL
)
3317 prevdef
->compression
= newdef
->compression
;
3318 else if (newdef
->compression
!= NULL
)
3320 if (strcmp(prevdef
->compression
, newdef
->compression
) != 0)
3322 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3323 errmsg("column \"%s\" has a compression method conflict",
3325 errdetail("%s versus %s",
3326 prevdef
->compression
, newdef
->compression
)));
3330 * Check for GENERATED conflicts
3332 if (prevdef
->generated
!= newdef
->generated
)
3334 (errcode(ERRCODE_DATATYPE_MISMATCH
),
3335 errmsg("inherited column \"%s\" has a generation conflict",
3339 * Default and other constraints are handled by the caller.
3342 prevdef
->inhcount
++;
3343 if (prevdef
->inhcount
< 0)
3345 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
3346 errmsg("too many inheritance parents"));
3352 * StoreCatalogInheritance
3353 * Updates the system catalogs with proper inheritance information.
3355 * supers is a list of the OIDs of the new relation's direct ancestors.
3358 StoreCatalogInheritance(Oid relationId
, List
*supers
,
3359 bool child_is_partition
)
3368 Assert(OidIsValid(relationId
));
3374 * Store INHERITS information in pg_inherits using direct ancestors only.
3375 * Also enter dependencies on the direct ancestors, and make sure they are
3376 * marked with relhassubclass = true.
3378 * (Once upon a time, both direct and indirect ancestors were found here
3379 * and then entered into pg_ipl. Since that catalog doesn't exist
3380 * anymore, there's no need to look for indirect ancestors.)
3382 relation
= table_open(InheritsRelationId
, RowExclusiveLock
);
3385 foreach(entry
, supers
)
3387 Oid parentOid
= lfirst_oid(entry
);
3389 StoreCatalogInheritance1(relationId
, parentOid
, seqNumber
, relation
,
3390 child_is_partition
);
3394 table_close(relation
, RowExclusiveLock
);
3398 * Make catalog entries showing relationId as being an inheritance child
3399 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3402 StoreCatalogInheritance1(Oid relationId
, Oid parentOid
,
3403 int32 seqNumber
, Relation inhRelation
,
3404 bool child_is_partition
)
3406 ObjectAddress childobject
,
3409 /* store the pg_inherits row */
3410 StoreSingleInheritance(relationId
, parentOid
, seqNumber
);
3413 * Store a dependency too
3415 parentobject
.classId
= RelationRelationId
;
3416 parentobject
.objectId
= parentOid
;
3417 parentobject
.objectSubId
= 0;
3418 childobject
.classId
= RelationRelationId
;
3419 childobject
.objectId
= relationId
;
3420 childobject
.objectSubId
= 0;
3422 recordDependencyOn(&childobject
, &parentobject
,
3423 child_dependency_type(child_is_partition
));
3426 * Post creation hook of this inheritance. Since object_access_hook
3427 * doesn't take multiple object identifiers, we relay oid of parent
3428 * relation using auxiliary_id argument.
3430 InvokeObjectPostAlterHookArg(InheritsRelationId
,
3435 * Mark the parent as having subclasses.
3437 SetRelationHasSubclass(parentOid
, true);
3441 * Look for an existing column entry with the given name.
3443 * Returns the index (starting with 1) if attribute already exists in columns,
3447 findAttrByName(const char *attributeName
, const List
*columns
)
3452 foreach(lc
, columns
)
3454 if (strcmp(attributeName
, lfirst_node(ColumnDef
, lc
)->colname
) == 0)
3464 * SetRelationHasSubclass
3465 * Set the value of the relation's relhassubclass field in pg_class.
3467 * It's always safe to set this field to true, because all SQL commands are
3468 * ready to see true and then find no children. On the other hand, commands
3469 * generally assume zero children if this is false.
3471 * Caller must hold any self-exclusive lock until end of transaction. If the
3472 * new value is false, caller must have acquired that lock before reading the
3473 * evidence that justified the false value. That way, it properly waits if
3474 * another backend is simultaneously concluding no need to change the tuple
3475 * (new and old values are true).
3477 * NOTE: an important side-effect of this operation is that an SI invalidation
3478 * message is sent out to all backends --- including me --- causing plans
3479 * referencing the relation to be rebuilt with the new list of children.
3480 * This must happen even if we find that no change is needed in the pg_class
3484 SetRelationHasSubclass(Oid relationId
, bool relhassubclass
)
3486 Relation relationRelation
;
3488 Form_pg_class classtuple
;
3490 Assert(CheckRelationOidLockedByMe(relationId
,
3491 ShareUpdateExclusiveLock
, false) ||
3492 CheckRelationOidLockedByMe(relationId
,
3493 ShareRowExclusiveLock
, true));
3496 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3498 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
3499 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relationId
));
3500 if (!HeapTupleIsValid(tuple
))
3501 elog(ERROR
, "cache lookup failed for relation %u", relationId
);
3502 classtuple
= (Form_pg_class
) GETSTRUCT(tuple
);
3504 if (classtuple
->relhassubclass
!= relhassubclass
)
3506 classtuple
->relhassubclass
= relhassubclass
;
3507 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
3511 /* no need to change tuple, but force relcache rebuild anyway */
3512 CacheInvalidateRelcacheByTuple(tuple
);
3515 heap_freetuple(tuple
);
3516 table_close(relationRelation
, RowExclusiveLock
);
3520 * CheckRelationTableSpaceMove
3521 * Check if relation can be moved to new tablespace.
3523 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3525 * Returns true if the relation can be moved to the new tablespace; raises
3526 * an error if it is not possible to do the move; returns false if the move
3527 * would have no effect.
3530 CheckRelationTableSpaceMove(Relation rel
, Oid newTableSpaceId
)
3532 Oid oldTableSpaceId
;
3535 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3538 oldTableSpaceId
= rel
->rd_rel
->reltablespace
;
3539 if (newTableSpaceId
== oldTableSpaceId
||
3540 (newTableSpaceId
== MyDatabaseTableSpace
&& oldTableSpaceId
== 0))
3544 * We cannot support moving mapped relations into different tablespaces.
3545 * (In particular this eliminates all shared catalogs.)
3547 if (RelationIsMapped(rel
))
3549 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3550 errmsg("cannot move system relation \"%s\"",
3551 RelationGetRelationName(rel
))));
3553 /* Cannot move a non-shared relation into pg_global */
3554 if (newTableSpaceId
== GLOBALTABLESPACE_OID
)
3556 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3557 errmsg("only shared relations can be placed in pg_global tablespace")));
3560 * Do not allow moving temp tables of other backends ... their local
3561 * buffer manager is not going to cope.
3563 if (RELATION_IS_OTHER_TEMP(rel
))
3565 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3566 errmsg("cannot move temporary tables of other sessions")));
3572 * SetRelationTableSpace
3573 * Set new reltablespace and relfilenumber in pg_class entry.
3575 * newTableSpaceId is the new tablespace for the relation, and
3576 * newRelFilenumber its new filenumber. If newRelFilenumber is
3577 * InvalidRelFileNumber, this field is not updated.
3579 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3581 * The caller of this routine had better check if a relation can be
3582 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3583 * first, and is responsible for making the change visible with
3584 * CommandCounterIncrement().
3587 SetRelationTableSpace(Relation rel
,
3588 Oid newTableSpaceId
,
3589 RelFileNumber newRelFilenumber
)
3593 Form_pg_class rd_rel
;
3594 Oid reloid
= RelationGetRelid(rel
);
3596 Assert(CheckRelationTableSpaceMove(rel
, newTableSpaceId
));
3598 /* Get a modifiable copy of the relation's pg_class row. */
3599 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
3601 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
3602 if (!HeapTupleIsValid(tuple
))
3603 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
3604 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
3606 /* Update the pg_class row. */
3607 rd_rel
->reltablespace
= (newTableSpaceId
== MyDatabaseTableSpace
) ?
3608 InvalidOid
: newTableSpaceId
;
3609 if (RelFileNumberIsValid(newRelFilenumber
))
3610 rd_rel
->relfilenode
= newRelFilenumber
;
3611 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
3614 * Record dependency on tablespace. This is only required for relations
3615 * that have no physical storage.
3617 if (!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
))
3618 changeDependencyOnTablespace(RelationRelationId
, reloid
,
3619 rd_rel
->reltablespace
);
3621 heap_freetuple(tuple
);
3622 table_close(pg_class
, RowExclusiveLock
);
3626 * renameatt_check - basic sanity checks before attribute rename
3629 renameatt_check(Oid myrelid
, Form_pg_class classform
, bool recursing
)
3631 char relkind
= classform
->relkind
;
3633 if (classform
->reloftype
&& !recursing
)
3635 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3636 errmsg("cannot rename column of typed table")));
3639 * Renaming the columns of sequences or toast tables doesn't actually
3640 * break anything from the system's point of view, since internal
3641 * references are by attnum. But it doesn't seem right to allow users to
3642 * change names that are hardcoded into the system, hence the following
3645 if (relkind
!= RELKIND_RELATION
&&
3646 relkind
!= RELKIND_VIEW
&&
3647 relkind
!= RELKIND_MATVIEW
&&
3648 relkind
!= RELKIND_COMPOSITE_TYPE
&&
3649 relkind
!= RELKIND_INDEX
&&
3650 relkind
!= RELKIND_PARTITIONED_INDEX
&&
3651 relkind
!= RELKIND_FOREIGN_TABLE
&&
3652 relkind
!= RELKIND_PARTITIONED_TABLE
)
3654 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
3655 errmsg("cannot rename columns of relation \"%s\"",
3656 NameStr(classform
->relname
)),
3657 errdetail_relkind_not_supported(relkind
)));
3660 * permissions checking. only the owner of a class can change its schema.
3662 if (!object_ownercheck(RelationRelationId
, myrelid
, GetUserId()))
3663 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(myrelid
)),
3664 NameStr(classform
->relname
));
3665 if (!allowSystemTableMods
&& IsSystemClass(myrelid
, classform
))
3667 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
3668 errmsg("permission denied: \"%s\" is a system catalog",
3669 NameStr(classform
->relname
))));
3673 * renameatt_internal - workhorse for renameatt
3675 * Return value is the attribute number in the 'myrelid' relation.
3678 renameatt_internal(Oid myrelid
,
3679 const char *oldattname
,
3680 const char *newattname
,
3683 int expected_parents
,
3684 DropBehavior behavior
)
3686 Relation targetrelation
;
3687 Relation attrelation
;
3689 Form_pg_attribute attform
;
3693 * Grab an exclusive lock on the target table, which we will NOT release
3694 * until end of transaction.
3696 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3697 renameatt_check(myrelid
, RelationGetForm(targetrelation
), recursing
);
3700 * if the 'recurse' flag is set then we are supposed to rename this
3701 * attribute in all classes that inherit from 'relname' (as well as in
3704 * any permissions or problems with duplicate attributes will cause the
3705 * whole transaction to abort, which is what we want -- all or nothing.
3715 * we need the number of parents for each child so that the recursive
3716 * calls to renameatt() can determine whether there are any parents
3717 * outside the inheritance hierarchy being processed.
3719 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3723 * find_all_inheritors does the recursive search of the inheritance
3724 * hierarchy, so all we have to do is process all of the relids in the
3725 * list that it returns.
3727 forboth(lo
, child_oids
, li
, child_numparents
)
3729 Oid childrelid
= lfirst_oid(lo
);
3730 int numparents
= lfirst_int(li
);
3732 if (childrelid
== myrelid
)
3734 /* note we need not recurse again */
3735 renameatt_internal(childrelid
, oldattname
, newattname
, false, true, numparents
, behavior
);
3741 * If we are told not to recurse, there had better not be any child
3742 * tables; else the rename would put them out of step.
3744 * expected_parents will only be 0 if we are not already recursing.
3746 if (expected_parents
== 0 &&
3747 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3749 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3750 errmsg("inherited column \"%s\" must be renamed in child tables too",
3754 /* rename attributes in typed tables of composite type */
3755 if (targetrelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
3760 child_oids
= find_typed_table_dependencies(targetrelation
->rd_rel
->reltype
,
3761 RelationGetRelationName(targetrelation
),
3764 foreach(lo
, child_oids
)
3765 renameatt_internal(lfirst_oid(lo
), oldattname
, newattname
, true, true, 0, behavior
);
3768 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
3770 atttup
= SearchSysCacheCopyAttName(myrelid
, oldattname
);
3771 if (!HeapTupleIsValid(atttup
))
3773 (errcode(ERRCODE_UNDEFINED_COLUMN
),
3774 errmsg("column \"%s\" does not exist",
3776 attform
= (Form_pg_attribute
) GETSTRUCT(atttup
);
3778 attnum
= attform
->attnum
;
3781 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3782 errmsg("cannot rename system column \"%s\"",
3786 * if the attribute is inherited, forbid the renaming. if this is a
3787 * top-level call to renameatt(), then expected_parents will be 0, so the
3788 * effect of this code will be to prohibit the renaming if the attribute
3789 * is inherited at all. if this is a recursive call to renameatt(),
3790 * expected_parents will be the number of parents the current relation has
3791 * within the inheritance hierarchy being processed, so we'll prohibit the
3792 * renaming only if there are additional parents from elsewhere.
3794 if (attform
->attinhcount
> expected_parents
)
3796 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3797 errmsg("cannot rename inherited column \"%s\"",
3800 /* new name should not already exist */
3801 (void) check_for_column_name_collision(targetrelation
, newattname
, false);
3803 /* apply the update */
3804 namestrcpy(&(attform
->attname
), newattname
);
3806 CatalogTupleUpdate(attrelation
, &atttup
->t_self
, atttup
);
3808 InvokeObjectPostAlterHook(RelationRelationId
, myrelid
, attnum
);
3810 heap_freetuple(atttup
);
3812 table_close(attrelation
, RowExclusiveLock
);
3814 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3820 * Perform permissions and integrity checks before acquiring a relation lock.
3823 RangeVarCallbackForRenameAttribute(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
3829 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
3830 if (!HeapTupleIsValid(tuple
))
3831 return; /* concurrently dropped */
3832 form
= (Form_pg_class
) GETSTRUCT(tuple
);
3833 renameatt_check(relid
, form
, false);
3834 ReleaseSysCache(tuple
);
3838 * renameatt - changes the name of an attribute in a relation
3840 * The returned ObjectAddress is that of the renamed column.
3843 renameatt(RenameStmt
*stmt
)
3847 ObjectAddress address
;
3849 /* lock level taken here should match renameatt_internal */
3850 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
3851 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
3852 RangeVarCallbackForRenameAttribute
,
3855 if (!OidIsValid(relid
))
3858 (errmsg("relation \"%s\" does not exist, skipping",
3859 stmt
->relation
->relname
)));
3860 return InvalidObjectAddress
;
3864 renameatt_internal(relid
,
3865 stmt
->subname
, /* old att name */
3866 stmt
->newname
, /* new att name */
3867 stmt
->relation
->inh
, /* recursive? */
3868 false, /* recursing? */
3869 0, /* expected inhcount */
3872 ObjectAddressSubSet(address
, RelationRelationId
, relid
, attnum
);
3878 * same logic as renameatt_internal
3880 static ObjectAddress
3881 rename_constraint_internal(Oid myrelid
,
3883 const char *oldconname
,
3884 const char *newconname
,
3887 int expected_parents
)
3889 Relation targetrelation
= NULL
;
3892 Form_pg_constraint con
;
3893 ObjectAddress address
;
3895 Assert(!myrelid
|| !mytypid
);
3899 constraintOid
= get_domain_constraint_oid(mytypid
, oldconname
, false);
3903 targetrelation
= relation_open(myrelid
, AccessExclusiveLock
);
3906 * don't tell it whether we're recursing; we allow changing typed
3909 renameatt_check(myrelid
, RelationGetForm(targetrelation
), false);
3911 constraintOid
= get_relation_constraint_oid(myrelid
, oldconname
, false);
3914 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constraintOid
));
3915 if (!HeapTupleIsValid(tuple
))
3916 elog(ERROR
, "cache lookup failed for constraint %u",
3918 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
3920 if (myrelid
&& con
->contype
== CONSTRAINT_CHECK
&& !con
->connoinherit
)
3929 child_oids
= find_all_inheritors(myrelid
, AccessExclusiveLock
,
3932 forboth(lo
, child_oids
, li
, child_numparents
)
3934 Oid childrelid
= lfirst_oid(lo
);
3935 int numparents
= lfirst_int(li
);
3937 if (childrelid
== myrelid
)
3940 rename_constraint_internal(childrelid
, InvalidOid
, oldconname
, newconname
, false, true, numparents
);
3945 if (expected_parents
== 0 &&
3946 find_inheritance_children(myrelid
, NoLock
) != NIL
)
3948 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3949 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3953 if (con
->coninhcount
> expected_parents
)
3955 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
3956 errmsg("cannot rename inherited constraint \"%s\"",
3961 && (con
->contype
== CONSTRAINT_PRIMARY
3962 || con
->contype
== CONSTRAINT_UNIQUE
3963 || con
->contype
== CONSTRAINT_EXCLUSION
))
3964 /* rename the index; this renames the constraint as well */
3965 RenameRelationInternal(con
->conindid
, newconname
, false, true);
3967 RenameConstraintById(constraintOid
, newconname
);
3969 ObjectAddressSet(address
, ConstraintRelationId
, constraintOid
);
3971 ReleaseSysCache(tuple
);
3976 * Invalidate relcache so as others can see the new constraint name.
3978 CacheInvalidateRelcache(targetrelation
);
3980 relation_close(targetrelation
, NoLock
); /* close rel but keep lock */
3987 RenameConstraint(RenameStmt
*stmt
)
3989 Oid relid
= InvalidOid
;
3990 Oid typid
= InvalidOid
;
3992 if (stmt
->renameType
== OBJECT_DOMCONSTRAINT
)
3997 typid
= typenameTypeId(NULL
, makeTypeNameFromNameList(castNode(List
, stmt
->object
)));
3998 rel
= table_open(TypeRelationId
, RowExclusiveLock
);
3999 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(typid
));
4000 if (!HeapTupleIsValid(tup
))
4001 elog(ERROR
, "cache lookup failed for type %u", typid
);
4002 checkDomainOwner(tup
);
4003 ReleaseSysCache(tup
);
4004 table_close(rel
, NoLock
);
4008 /* lock level taken here should match rename_constraint_internal */
4009 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
4010 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4011 RangeVarCallbackForRenameAttribute
,
4013 if (!OidIsValid(relid
))
4016 (errmsg("relation \"%s\" does not exist, skipping",
4017 stmt
->relation
->relname
)));
4018 return InvalidObjectAddress
;
4023 rename_constraint_internal(relid
, typid
,
4027 stmt
->relation
->inh
), /* recursive? */
4028 false, /* recursing? */
4029 0 /* expected inhcount */ );
4033 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4037 RenameRelation(RenameStmt
*stmt
)
4039 bool is_index_stmt
= stmt
->renameType
== OBJECT_INDEX
;
4041 ObjectAddress address
;
4044 * Grab an exclusive lock on the target table, index, sequence, view,
4045 * materialized view, or foreign table, which we will NOT release until
4046 * end of transaction.
4048 * Lock level used here should match RenameRelationInternal, to avoid lock
4049 * escalation. However, because ALTER INDEX can be used with any relation
4050 * type, we mustn't believe without verification.
4058 lockmode
= is_index_stmt
? ShareUpdateExclusiveLock
: AccessExclusiveLock
;
4060 relid
= RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4061 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4062 RangeVarCallbackForAlterRelation
,
4065 if (!OidIsValid(relid
))
4068 (errmsg("relation \"%s\" does not exist, skipping",
4069 stmt
->relation
->relname
)));
4070 return InvalidObjectAddress
;
4074 * We allow mismatched statement and object types (e.g., ALTER INDEX
4075 * to rename a table), but we might've used the wrong lock level. If
4076 * that happens, retry with the correct lock level. We don't bother
4077 * if we already acquired AccessExclusiveLock with an index, however.
4079 relkind
= get_rel_relkind(relid
);
4080 obj_is_index
= (relkind
== RELKIND_INDEX
||
4081 relkind
== RELKIND_PARTITIONED_INDEX
);
4082 if (obj_is_index
|| is_index_stmt
== obj_is_index
)
4085 UnlockRelationOid(relid
, lockmode
);
4086 is_index_stmt
= obj_is_index
;
4090 RenameRelationInternal(relid
, stmt
->newname
, false, is_index_stmt
);
4092 ObjectAddressSet(address
, RelationRelationId
, relid
);
4098 * RenameRelationInternal - change the name of a relation
4101 RenameRelationInternal(Oid myrelid
, const char *newrelname
, bool is_internal
, bool is_index
)
4103 Relation targetrelation
;
4104 Relation relrelation
; /* for RELATION relation */
4106 Form_pg_class relform
;
4110 * Grab a lock on the target relation, which we will NOT release until end
4111 * of transaction. We need at least a self-exclusive lock so that
4112 * concurrent DDL doesn't overwrite the rename if they start updating
4113 * while still seeing the old version. The lock also guards against
4114 * triggering relcache reloads in concurrent sessions, which might not
4115 * handle this information changing under them. For indexes, we can use a
4116 * reduced lock level because RelationReloadIndexInfo() handles indexes
4119 targetrelation
= relation_open(myrelid
, is_index
? ShareUpdateExclusiveLock
: AccessExclusiveLock
);
4120 namespaceId
= RelationGetNamespace(targetrelation
);
4123 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4125 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4127 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4128 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4129 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4130 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4132 if (get_relname_relid(newrelname
, namespaceId
) != InvalidOid
)
4134 (errcode(ERRCODE_DUPLICATE_TABLE
),
4135 errmsg("relation \"%s\" already exists",
4139 * RenameRelation is careful not to believe the caller's idea of the
4140 * relation kind being handled. We don't have to worry about this, but
4141 * let's not be totally oblivious to it. We can process an index as
4142 * not-an-index, but not the other way around.
4145 is_index
== (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4146 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
));
4149 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4150 * because it's a copy...)
4152 namestrcpy(&(relform
->relname
), newrelname
);
4154 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4156 InvokeObjectPostAlterHookArg(RelationRelationId
, myrelid
, 0,
4157 InvalidOid
, is_internal
);
4159 heap_freetuple(reltup
);
4160 table_close(relrelation
, RowExclusiveLock
);
4163 * Also rename the associated type, if any.
4165 if (OidIsValid(targetrelation
->rd_rel
->reltype
))
4166 RenameTypeInternal(targetrelation
->rd_rel
->reltype
,
4167 newrelname
, namespaceId
);
4170 * Also rename the associated constraint, if any.
4172 if (targetrelation
->rd_rel
->relkind
== RELKIND_INDEX
||
4173 targetrelation
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
4175 Oid constraintId
= get_index_constraint(myrelid
);
4177 if (OidIsValid(constraintId
))
4178 RenameConstraintById(constraintId
, newrelname
);
4182 * Close rel, but keep lock!
4184 relation_close(targetrelation
, NoLock
);
4188 * ResetRelRewrite - reset relrewrite
4191 ResetRelRewrite(Oid myrelid
)
4193 Relation relrelation
; /* for RELATION relation */
4195 Form_pg_class relform
;
4198 * Find relation's pg_class tuple.
4200 relrelation
= table_open(RelationRelationId
, RowExclusiveLock
);
4202 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
4203 if (!HeapTupleIsValid(reltup
)) /* shouldn't happen */
4204 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
4205 relform
= (Form_pg_class
) GETSTRUCT(reltup
);
4208 * Update pg_class tuple.
4210 relform
->relrewrite
= InvalidOid
;
4212 CatalogTupleUpdate(relrelation
, &reltup
->t_self
, reltup
);
4214 heap_freetuple(reltup
);
4215 table_close(relrelation
, RowExclusiveLock
);
4219 * Disallow ALTER TABLE (and similar commands) when the current backend has
4220 * any open reference to the target table besides the one just acquired by
4221 * the calling command; this implies there's an open cursor or active plan.
4222 * We need this check because our lock doesn't protect us against stomping
4223 * on our own foot, only other people's feet!
4225 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4226 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4227 * possibly be relaxed to only error out for certain types of alterations.
4228 * But the use-case for allowing any of these things is not obvious, so we
4229 * won't work hard at it for now.
4231 * We also reject these commands if there are any pending AFTER trigger events
4232 * for the rel. This is certainly necessary for the rewriting variants of
4233 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4234 * events would try to fetch the wrong tuples. It might be overly cautious
4235 * in other cases, but again it seems better to err on the side of paranoia.
4237 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4238 * we are worried about active indexscans on the index. The trigger-event
4239 * check can be skipped, since we are doing no damage to the parent table.
4241 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4244 CheckTableNotInUse(Relation rel
, const char *stmt
)
4246 int expected_refcnt
;
4248 expected_refcnt
= rel
->rd_isnailed
? 2 : 1;
4249 if (rel
->rd_refcnt
!= expected_refcnt
)
4251 (errcode(ERRCODE_OBJECT_IN_USE
),
4252 /* translator: first %s is a SQL command, eg ALTER TABLE */
4253 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4254 stmt
, RelationGetRelationName(rel
))));
4256 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
4257 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
4258 AfterTriggerPendingOnRel(RelationGetRelid(rel
)))
4260 (errcode(ERRCODE_OBJECT_IN_USE
),
4261 /* translator: first %s is a SQL command, eg ALTER TABLE */
4262 errmsg("cannot %s \"%s\" because it has pending trigger events",
4263 stmt
, RelationGetRelationName(rel
))));
4267 * CheckAlterTableIsSafe
4268 * Verify that it's safe to allow ALTER TABLE on this relation.
4270 * This consists of CheckTableNotInUse() plus a check that the relation
4271 * isn't another session's temp table. We must split out the temp-table
4272 * check because there are callers of CheckTableNotInUse() that don't want
4273 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4274 * an orphaned temp schema.) Compare truncate_check_activity().
4277 CheckAlterTableIsSafe(Relation rel
)
4280 * Don't allow ALTER on temp tables of other backends. Their local buffer
4281 * manager is not going to cope if we need to change the table's contents.
4282 * Even if we don't, there may be optimizations that assume temp tables
4283 * aren't subject to such interference.
4285 if (RELATION_IS_OTHER_TEMP(rel
))
4287 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4288 errmsg("cannot alter temporary tables of other sessions")));
4291 * Also check for active uses of the relation in the current transaction,
4292 * including open scans and pending AFTER trigger events.
4294 CheckTableNotInUse(rel
, "ALTER TABLE");
4298 * AlterTableLookupRelation
4299 * Look up, and lock, the OID for the relation named by an alter table
4303 AlterTableLookupRelation(AlterTableStmt
*stmt
, LOCKMODE lockmode
)
4305 return RangeVarGetRelidExtended(stmt
->relation
, lockmode
,
4306 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
4307 RangeVarCallbackForAlterRelation
,
4313 * Execute ALTER TABLE, which can be a list of subcommands
4315 * ALTER TABLE is performed in three phases:
4316 * 1. Examine subcommands and perform pre-transformation checking.
4317 * 2. Validate and transform subcommands, and update system catalogs.
4318 * 3. Scan table(s) to check new constraints, and optionally recopy
4319 * the data into new table(s).
4320 * Phase 3 is not performed unless one or more of the subcommands requires
4321 * it. The intention of this design is to allow multiple independent
4322 * updates of the table schema to be performed with only one pass over the
4325 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4326 * each table to be affected (there may be multiple affected tables if the
4327 * commands traverse a table inheritance hierarchy). Also we do preliminary
4328 * validation of the subcommands. Because earlier subcommands may change
4329 * the catalog state seen by later commands, there are limits to what can
4330 * be done in this phase. Generally, this phase acquires table locks,
4331 * checks permissions and relkind, and recurses to find child tables.
4333 * ATRewriteCatalogs performs phase 2 for each affected table.
4334 * Certain subcommands need to be performed before others to avoid
4335 * unnecessary conflicts; for example, DROP COLUMN should come before
4336 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4337 * lists, one for each logical "pass" of phase 2.
4339 * ATRewriteTables performs phase 3 for those tables that need it.
4341 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4342 * since phase 1 already does it. However, for certain subcommand types
4343 * it is only possible to determine how to recurse at phase 2 time; for
4344 * those cases, phase 1 sets the cmd->recurse flag.
4346 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4347 * the whole operation; we don't have to do anything special to clean up.
4349 * The caller must lock the relation, with an appropriate lock level
4350 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4351 * or higher. We pass the lock level down
4352 * so that we can apply it recursively to inherited tables. Note that the
4353 * lock level we want as we recurse might well be higher than required for
4354 * that specific subcommand. So we pass down the overall lock requirement,
4355 * rather than reassess it at lower levels.
4357 * The caller also provides a "context" which is to be passed back to
4358 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4359 * Some of the fields therein, such as the relid, are used here as well.
4362 AlterTable(AlterTableStmt
*stmt
, LOCKMODE lockmode
,
4363 AlterTableUtilityContext
*context
)
4367 /* Caller is required to provide an adequate lock. */
4368 rel
= relation_open(context
->relid
, NoLock
);
4370 CheckAlterTableIsSafe(rel
);
4372 ATController(stmt
, rel
, stmt
->cmds
, stmt
->relation
->inh
, lockmode
, context
);
4376 * AlterTableInternal
4378 * ALTER TABLE with target specified by OID
4380 * We do not reject if the relation is already open, because it's quite
4381 * likely that one or more layers of caller have it open. That means it
4382 * is unsafe to use this entry point for alterations that could break
4383 * existing query plans. On the assumption it's not used for such, we
4384 * don't have to reject pending AFTER triggers, either.
4386 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4387 * used for any subcommand types that require parse transformation or
4388 * could generate subcommands that have to be passed to ProcessUtility.
4391 AlterTableInternal(Oid relid
, List
*cmds
, bool recurse
)
4394 LOCKMODE lockmode
= AlterTableGetLockLevel(cmds
);
4396 rel
= relation_open(relid
, lockmode
);
4398 EventTriggerAlterTableRelid(relid
);
4400 ATController(NULL
, rel
, cmds
, recurse
, lockmode
, NULL
);
4404 * AlterTableGetLockLevel
4406 * Sets the overall lock level required for the supplied list of subcommands.
4407 * Policy for doing this set according to needs of AlterTable(), see
4408 * comments there for overall explanation.
4410 * Function is called before and after parsing, so it must give same
4411 * answer each time it is called. Some subcommands are transformed
4412 * into other subcommand types, so the transform must never be made to a
4413 * lower lock level than previously assigned. All transforms are noted below.
4415 * Since this is called before we lock the table we cannot use table metadata
4416 * to influence the type of lock we acquire.
4418 * There should be no lockmodes hardcoded into the subcommand functions. All
4419 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4420 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4421 * and does not travel through this section of code and cannot be combined with
4422 * any of the subcommands given here.
4424 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4425 * so any changes that might affect SELECTs running on standbys need to use
4426 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4427 * have a solution for that also.
4429 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4430 * that takes a lock less than AccessExclusiveLock can change object definitions
4431 * while pg_dump is running. Be careful to check that the appropriate data is
4432 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4433 * otherwise we might end up with an inconsistent dump that can't restore.
4436 AlterTableGetLockLevel(List
*cmds
)
4439 * This only works if we read catalog tables using MVCC snapshots.
4442 LOCKMODE lockmode
= ShareUpdateExclusiveLock
;
4446 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4447 LOCKMODE cmd_lockmode
= AccessExclusiveLock
; /* default for compiler */
4449 switch (cmd
->subtype
)
4452 * These subcommands rewrite the heap, so require full locks.
4454 case AT_AddColumn
: /* may rewrite heap, in some cases and visible
4456 case AT_SetAccessMethod
: /* must rewrite heap */
4457 case AT_SetTableSpace
: /* must rewrite heap */
4458 case AT_AlterColumnType
: /* must rewrite heap */
4459 cmd_lockmode
= AccessExclusiveLock
;
4463 * These subcommands may require addition of toast tables. If
4464 * we add a toast table to a table currently being scanned, we
4465 * might miss data added to the new toast table by concurrent
4466 * insert transactions.
4468 case AT_SetStorage
: /* may add toast tables, see
4469 * ATRewriteCatalogs() */
4470 cmd_lockmode
= AccessExclusiveLock
;
4474 * Removing constraints can affect SELECTs that have been
4475 * optimized assuming the constraint holds true. See also
4476 * CloneFkReferenced.
4478 case AT_DropConstraint
: /* as DROP INDEX */
4479 case AT_DropNotNull
: /* may change some SQL plans */
4480 cmd_lockmode
= AccessExclusiveLock
;
4484 * Subcommands that may be visible to concurrent SELECTs
4486 case AT_DropColumn
: /* change visible to SELECT */
4487 case AT_AddColumnToView
: /* CREATE VIEW */
4488 case AT_DropOids
: /* used to equiv to DropColumn */
4489 case AT_EnableAlwaysRule
: /* may change SELECT rules */
4490 case AT_EnableReplicaRule
: /* may change SELECT rules */
4491 case AT_EnableRule
: /* may change SELECT rules */
4492 case AT_DisableRule
: /* may change SELECT rules */
4493 cmd_lockmode
= AccessExclusiveLock
;
4497 * Changing owner may remove implicit SELECT privileges
4499 case AT_ChangeOwner
: /* change visible to SELECT */
4500 cmd_lockmode
= AccessExclusiveLock
;
4504 * Changing foreign table options may affect optimization.
4506 case AT_GenericOptions
:
4507 case AT_AlterColumnGenericOptions
:
4508 cmd_lockmode
= AccessExclusiveLock
;
4512 * These subcommands affect write operations only.
4515 case AT_EnableAlwaysTrig
:
4516 case AT_EnableReplicaTrig
:
4517 case AT_EnableTrigAll
:
4518 case AT_EnableTrigUser
:
4519 case AT_DisableTrig
:
4520 case AT_DisableTrigAll
:
4521 case AT_DisableTrigUser
:
4522 cmd_lockmode
= ShareRowExclusiveLock
;
4526 * These subcommands affect write operations only. XXX
4527 * Theoretically, these could be ShareRowExclusiveLock.
4529 case AT_ColumnDefault
:
4530 case AT_CookedColumnDefault
:
4531 case AT_AlterConstraint
:
4532 case AT_AddIndex
: /* from ADD CONSTRAINT */
4533 case AT_AddIndexConstraint
:
4534 case AT_ReplicaIdentity
:
4536 case AT_EnableRowSecurity
:
4537 case AT_DisableRowSecurity
:
4538 case AT_ForceRowSecurity
:
4539 case AT_NoForceRowSecurity
:
4540 case AT_AddIdentity
:
4541 case AT_DropIdentity
:
4542 case AT_SetIdentity
:
4543 case AT_SetExpression
:
4544 case AT_DropExpression
:
4545 case AT_SetCompression
:
4546 cmd_lockmode
= AccessExclusiveLock
;
4549 case AT_AddConstraint
:
4550 case AT_ReAddConstraint
: /* becomes AT_AddConstraint */
4551 case AT_ReAddDomainConstraint
: /* becomes AT_AddConstraint */
4552 if (IsA(cmd
->def
, Constraint
))
4554 Constraint
*con
= (Constraint
*) cmd
->def
;
4556 switch (con
->contype
)
4558 case CONSTR_EXCLUSION
:
4559 case CONSTR_PRIMARY
:
4563 * Cases essentially the same as CREATE INDEX. We
4564 * could reduce the lock strength to ShareLock if
4565 * we can work out how to allow concurrent catalog
4566 * updates. XXX Might be set down to
4567 * ShareRowExclusiveLock but requires further
4570 cmd_lockmode
= AccessExclusiveLock
;
4572 case CONSTR_FOREIGN
:
4575 * We add triggers to both tables when we add a
4576 * Foreign Key, so the lock level must be at least
4577 * as strong as CREATE TRIGGER.
4579 cmd_lockmode
= ShareRowExclusiveLock
;
4583 cmd_lockmode
= AccessExclusiveLock
;
4589 * These subcommands affect inheritance behaviour. Queries
4590 * started before us will continue to see the old inheritance
4591 * behaviour, while queries started after we commit will see
4592 * new behaviour. No need to prevent reads or writes to the
4593 * subtable while we hook it up though. Changing the TupDesc
4594 * may be a problem, so keep highest lock.
4597 case AT_DropInherit
:
4598 cmd_lockmode
= AccessExclusiveLock
;
4602 * These subcommands affect implicit row type conversion. They
4603 * have affects similar to CREATE/DROP CAST on queries. don't
4604 * provide for invalidating parse trees as a result of such
4605 * changes, so we keep these at AccessExclusiveLock.
4609 cmd_lockmode
= AccessExclusiveLock
;
4613 * Only used by CREATE OR REPLACE VIEW which must conflict
4614 * with an SELECTs currently using the view.
4616 case AT_ReplaceRelOptions
:
4617 cmd_lockmode
= AccessExclusiveLock
;
4621 * These subcommands affect general strategies for performance
4622 * and maintenance, though don't change the semantic results
4623 * from normal data reads and writes. Delaying an ALTER TABLE
4624 * behind currently active writes only delays the point where
4625 * the new strategy begins to take effect, so there is no
4626 * benefit in waiting. In this case the minimum restriction
4627 * applies: we don't currently allow concurrent catalog
4630 case AT_SetStatistics
: /* Uses MVCC in getTableAttrs() */
4631 case AT_ClusterOn
: /* Uses MVCC in getIndexes() */
4632 case AT_DropCluster
: /* Uses MVCC in getIndexes() */
4633 case AT_SetOptions
: /* Uses MVCC in getTableAttrs() */
4634 case AT_ResetOptions
: /* Uses MVCC in getTableAttrs() */
4635 cmd_lockmode
= ShareUpdateExclusiveLock
;
4639 case AT_SetUnLogged
:
4640 cmd_lockmode
= AccessExclusiveLock
;
4643 case AT_ValidateConstraint
: /* Uses MVCC in getConstraints() */
4644 cmd_lockmode
= ShareUpdateExclusiveLock
;
4648 * Rel options are more complex than first appears. Options
4649 * are set here for tables, views and indexes; for historical
4650 * reasons these can all be used with ALTER TABLE, so we can't
4651 * decide between them using the basic grammar.
4653 case AT_SetRelOptions
: /* Uses MVCC in getIndexes() and
4655 case AT_ResetRelOptions
: /* Uses MVCC in getIndexes() and
4657 cmd_lockmode
= AlterTableGetRelOptionsLockLevel((List
*) cmd
->def
);
4660 case AT_AttachPartition
:
4661 cmd_lockmode
= ShareUpdateExclusiveLock
;
4664 case AT_DetachPartition
:
4665 if (((PartitionCmd
*) cmd
->def
)->concurrent
)
4666 cmd_lockmode
= ShareUpdateExclusiveLock
;
4668 cmd_lockmode
= AccessExclusiveLock
;
4671 case AT_DetachPartitionFinalize
:
4672 cmd_lockmode
= ShareUpdateExclusiveLock
;
4675 case AT_SplitPartition
:
4676 cmd_lockmode
= AccessExclusiveLock
;
4679 case AT_MergePartitions
:
4680 cmd_lockmode
= AccessExclusiveLock
;
4683 case AT_CheckNotNull
:
4686 * This only examines the table's schema; but lock must be
4687 * strong enough to prevent concurrent DROP NOT NULL.
4689 cmd_lockmode
= AccessShareLock
;
4693 elog(ERROR
, "unrecognized alter table type: %d",
4694 (int) cmd
->subtype
);
4699 * Take the greatest lockmode from any subcommand
4701 if (cmd_lockmode
> lockmode
)
4702 lockmode
= cmd_lockmode
;
4709 * ATController provides top level control over the phases.
4711 * parsetree is passed in to allow it to be passed to event triggers
4715 ATController(AlterTableStmt
*parsetree
,
4716 Relation rel
, List
*cmds
, bool recurse
, LOCKMODE lockmode
,
4717 AlterTableUtilityContext
*context
)
4722 /* Phase 1: preliminary examination of commands, create work queue */
4725 AlterTableCmd
*cmd
= (AlterTableCmd
*) lfirst(lcmd
);
4727 ATPrepCmd(&wqueue
, rel
, cmd
, recurse
, false, lockmode
, context
);
4730 /* Close the relation, but keep lock until commit */
4731 relation_close(rel
, NoLock
);
4733 /* Phase 2: update system catalogs */
4734 ATRewriteCatalogs(&wqueue
, lockmode
, context
);
4736 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4737 ATRewriteTables(parsetree
, &wqueue
, lockmode
, context
);
4743 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4744 * recursion and permission checks.
4746 * Caller must have acquired appropriate lock type on relation already.
4747 * This lock should be held until commit.
4750 ATPrepCmd(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
4751 bool recurse
, bool recursing
, LOCKMODE lockmode
,
4752 AlterTableUtilityContext
*context
)
4754 AlteredTableInfo
*tab
;
4755 AlterTablePass pass
= AT_PASS_UNSET
;
4757 /* Find or create work queue entry for this table */
4758 tab
= ATGetQueueEntry(wqueue
, rel
);
4761 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4762 * partitions that are pending detach.
4764 if (rel
->rd_rel
->relispartition
&&
4765 cmd
->subtype
!= AT_DetachPartitionFinalize
&&
4766 PartitionHasPendingDetach(RelationGetRelid(rel
)))
4768 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
4769 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4770 RelationGetRelationName(rel
)),
4771 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4774 * Copy the original subcommand for each table, so we can scribble on it.
4775 * This avoids conflicts when different child tables need to make
4776 * different parse transformations (for example, the same column may have
4777 * different column numbers in different children).
4779 cmd
= copyObject(cmd
);
4782 * Do permissions and relkind checking, recursion to child tables if
4783 * needed, and any additional phase-1 processing needed. (But beware of
4784 * adding any processing that looks at table details that another
4785 * subcommand could change. In some cases we reject multiple subcommands
4786 * that could try to change the same state in contrary ways.)
4788 switch (cmd
->subtype
)
4790 case AT_AddColumn
: /* ADD COLUMN */
4791 ATSimplePermissions(cmd
->subtype
, rel
,
4792 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4793 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, false, cmd
,
4795 /* Recursion occurs during execution phase */
4796 pass
= AT_PASS_ADD_COL
;
4798 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
4799 ATSimplePermissions(cmd
->subtype
, rel
, ATT_VIEW
);
4800 ATPrepAddColumn(wqueue
, rel
, recurse
, recursing
, true, cmd
,
4802 /* Recursion occurs during execution phase */
4803 pass
= AT_PASS_ADD_COL
;
4805 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
4808 * We allow defaults on views so that INSERT into a view can have
4809 * default-ish behavior. This works because the rewriter
4810 * substitutes default values into INSERTs before it expands
4813 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4814 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4815 /* No command-specific prep needed */
4816 pass
= cmd
->def
? AT_PASS_ADD_OTHERCONSTR
: AT_PASS_DROP
;
4818 case AT_CookedColumnDefault
: /* add a pre-cooked default */
4819 /* This is currently used only in CREATE TABLE */
4820 /* (so the permission check really isn't necessary) */
4821 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4822 /* This command never recurses */
4823 pass
= AT_PASS_ADD_OTHERCONSTR
;
4825 case AT_AddIdentity
:
4826 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4827 /* Set up recursion for phase 2; no other prep needed */
4829 cmd
->recurse
= true;
4830 pass
= AT_PASS_ADD_OTHERCONSTR
;
4832 case AT_SetIdentity
:
4833 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4834 /* Set up recursion for phase 2; no other prep needed */
4836 cmd
->recurse
= true;
4837 /* This should run after AddIdentity, so do it in MISC pass */
4838 pass
= AT_PASS_MISC
;
4840 case AT_DropIdentity
:
4841 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_FOREIGN_TABLE
);
4842 /* Set up recursion for phase 2; no other prep needed */
4844 cmd
->recurse
= true;
4845 pass
= AT_PASS_DROP
;
4847 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
4848 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4849 ATPrepDropNotNull(rel
, recurse
, recursing
);
4850 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4851 pass
= AT_PASS_DROP
;
4853 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
4854 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4855 /* Need command-specific recursion decision */
4856 ATPrepSetNotNull(wqueue
, rel
, cmd
, recurse
, recursing
,
4858 pass
= AT_PASS_COL_ATTRS
;
4860 case AT_CheckNotNull
: /* check column is already marked NOT NULL */
4861 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4862 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4863 /* No command-specific prep needed */
4864 pass
= AT_PASS_COL_ATTRS
;
4866 case AT_SetExpression
: /* ALTER COLUMN SET EXPRESSION */
4867 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4868 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4869 pass
= AT_PASS_SET_EXPRESSION
;
4871 case AT_DropExpression
: /* ALTER COLUMN DROP EXPRESSION */
4872 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4873 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4874 ATPrepDropExpression(rel
, cmd
, recurse
, recursing
, lockmode
);
4875 pass
= AT_PASS_DROP
;
4877 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
4878 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
| ATT_PARTITIONED_INDEX
| ATT_FOREIGN_TABLE
);
4879 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4880 /* No command-specific prep needed */
4881 pass
= AT_PASS_MISC
;
4883 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
4884 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
4885 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4886 /* This command never recurses */
4887 pass
= AT_PASS_MISC
;
4889 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
4890 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_FOREIGN_TABLE
);
4891 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
4892 /* No command-specific prep needed */
4893 pass
= AT_PASS_MISC
;
4895 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
4896 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4897 /* This command never recurses */
4898 /* No command-specific prep needed */
4899 pass
= AT_PASS_MISC
;
4901 case AT_DropColumn
: /* DROP COLUMN */
4902 ATSimplePermissions(cmd
->subtype
, rel
,
4903 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4904 ATPrepDropColumn(wqueue
, rel
, recurse
, recursing
, cmd
,
4906 /* Recursion occurs during execution phase */
4907 pass
= AT_PASS_DROP
;
4909 case AT_AddIndex
: /* ADD INDEX */
4910 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4911 /* This command never recurses */
4912 /* No command-specific prep needed */
4913 pass
= AT_PASS_ADD_INDEX
;
4915 case AT_AddConstraint
: /* ADD CONSTRAINT */
4916 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4917 /* Recursion occurs during execution phase */
4918 /* No command-specific prep needed except saving recurse flag */
4920 cmd
->recurse
= true;
4921 pass
= AT_PASS_ADD_CONSTR
;
4923 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
4924 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
4925 /* This command never recurses */
4926 /* No command-specific prep needed */
4927 pass
= AT_PASS_ADD_INDEXCONSTR
;
4929 case AT_DropConstraint
: /* DROP CONSTRAINT */
4930 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
4931 ATCheckPartitionsNotInUse(rel
, lockmode
);
4932 /* Other recursion occurs during execution phase */
4933 /* No command-specific prep needed except saving recurse flag */
4935 cmd
->recurse
= true;
4936 pass
= AT_PASS_DROP
;
4938 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
4939 ATSimplePermissions(cmd
->subtype
, rel
,
4940 ATT_TABLE
| ATT_COMPOSITE_TYPE
| ATT_FOREIGN_TABLE
);
4941 /* See comments for ATPrepAlterColumnType */
4942 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, recurse
, lockmode
,
4943 AT_PASS_UNSET
, context
);
4944 Assert(cmd
!= NULL
);
4945 /* Performs own recursion */
4946 ATPrepAlterColumnType(wqueue
, tab
, rel
, recurse
, recursing
, cmd
,
4948 pass
= AT_PASS_ALTER_TYPE
;
4950 case AT_AlterColumnGenericOptions
:
4951 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
4952 /* This command never recurses */
4953 /* No command-specific prep needed */
4954 pass
= AT_PASS_MISC
;
4956 case AT_ChangeOwner
: /* ALTER OWNER */
4957 /* This command never recurses */
4958 /* No command-specific prep needed */
4959 pass
= AT_PASS_MISC
;
4961 case AT_ClusterOn
: /* CLUSTER ON */
4962 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
4963 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
4964 /* These commands never recurse */
4965 /* No command-specific prep needed */
4966 pass
= AT_PASS_MISC
;
4968 case AT_SetLogged
: /* SET LOGGED */
4969 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4970 if (tab
->chgPersistence
)
4972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4973 errmsg("cannot change persistence setting twice")));
4974 tab
->chgPersistence
= ATPrepChangePersistence(rel
, true);
4975 /* force rewrite if necessary; see comment in ATRewriteTables */
4976 if (tab
->chgPersistence
)
4978 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4979 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
4981 pass
= AT_PASS_MISC
;
4983 case AT_SetUnLogged
: /* SET UNLOGGED */
4984 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_SEQUENCE
);
4985 if (tab
->chgPersistence
)
4987 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4988 errmsg("cannot change persistence setting twice")));
4989 tab
->chgPersistence
= ATPrepChangePersistence(rel
, false);
4990 /* force rewrite if necessary; see comment in ATRewriteTables */
4991 if (tab
->chgPersistence
)
4993 tab
->rewrite
|= AT_REWRITE_ALTER_PERSISTENCE
;
4994 tab
->newrelpersistence
= RELPERSISTENCE_UNLOGGED
;
4996 pass
= AT_PASS_MISC
;
4998 case AT_DropOids
: /* SET WITHOUT OIDS */
4999 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5000 pass
= AT_PASS_DROP
;
5002 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5003 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5005 /* check if another access method change was already requested */
5006 if (tab
->chgAccessMethod
)
5008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5009 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5011 ATPrepSetAccessMethod(tab
, rel
, cmd
->name
);
5012 pass
= AT_PASS_MISC
; /* does not matter; no work in Phase 2 */
5014 case AT_SetTableSpace
: /* SET TABLESPACE */
5015 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
| ATT_INDEX
|
5016 ATT_PARTITIONED_INDEX
);
5017 /* This command never recurses */
5018 ATPrepSetTableSpace(tab
, rel
, cmd
->name
, lockmode
);
5019 pass
= AT_PASS_MISC
; /* doesn't actually matter */
5021 case AT_SetRelOptions
: /* SET (...) */
5022 case AT_ResetRelOptions
: /* RESET (...) */
5023 case AT_ReplaceRelOptions
: /* reset them all, then set just these */
5024 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_VIEW
| ATT_MATVIEW
| ATT_INDEX
);
5025 /* This command never recurses */
5026 /* No command-specific prep needed */
5027 pass
= AT_PASS_MISC
;
5029 case AT_AddInherit
: /* INHERIT */
5030 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5031 /* This command never recurses */
5032 ATPrepAddInherit(rel
);
5033 pass
= AT_PASS_MISC
;
5035 case AT_DropInherit
: /* NO INHERIT */
5036 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5037 /* This command never recurses */
5038 /* No command-specific prep needed */
5039 pass
= AT_PASS_MISC
;
5041 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5042 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5043 /* Recursion occurs during execution phase */
5044 pass
= AT_PASS_MISC
;
5046 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5047 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5048 /* Recursion occurs during execution phase */
5049 /* No command-specific prep needed except saving recurse flag */
5051 cmd
->recurse
= true;
5052 pass
= AT_PASS_MISC
;
5054 case AT_ReplicaIdentity
: /* REPLICA IDENTITY ... */
5055 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_MATVIEW
);
5056 pass
= AT_PASS_MISC
;
5057 /* This command never recurses */
5058 /* No command-specific prep needed */
5060 case AT_EnableTrig
: /* ENABLE TRIGGER variants */
5061 case AT_EnableAlwaysTrig
:
5062 case AT_EnableReplicaTrig
:
5063 case AT_EnableTrigAll
:
5064 case AT_EnableTrigUser
:
5065 case AT_DisableTrig
: /* DISABLE TRIGGER variants */
5066 case AT_DisableTrigAll
:
5067 case AT_DisableTrigUser
:
5068 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
5069 /* Set up recursion for phase 2; no other prep needed */
5071 cmd
->recurse
= true;
5072 pass
= AT_PASS_MISC
;
5074 case AT_EnableRule
: /* ENABLE/DISABLE RULE variants */
5075 case AT_EnableAlwaysRule
:
5076 case AT_EnableReplicaRule
:
5077 case AT_DisableRule
:
5078 case AT_AddOf
: /* OF */
5079 case AT_DropOf
: /* NOT OF */
5080 case AT_EnableRowSecurity
:
5081 case AT_DisableRowSecurity
:
5082 case AT_ForceRowSecurity
:
5083 case AT_NoForceRowSecurity
:
5084 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5085 /* These commands never recurse */
5086 /* No command-specific prep needed */
5087 pass
= AT_PASS_MISC
;
5089 case AT_GenericOptions
:
5090 ATSimplePermissions(cmd
->subtype
, rel
, ATT_FOREIGN_TABLE
);
5091 /* No command-specific prep needed */
5092 pass
= AT_PASS_MISC
;
5094 case AT_AttachPartition
:
5095 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
| ATT_PARTITIONED_INDEX
);
5096 /* No command-specific prep needed */
5097 pass
= AT_PASS_MISC
;
5099 case AT_DetachPartition
:
5100 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5101 /* No command-specific prep needed */
5102 pass
= AT_PASS_MISC
;
5104 case AT_DetachPartitionFinalize
:
5105 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5106 /* No command-specific prep needed */
5107 pass
= AT_PASS_MISC
;
5109 case AT_SplitPartition
:
5110 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5111 /* No command-specific prep needed */
5112 pass
= AT_PASS_MISC
;
5114 case AT_MergePartitions
:
5115 ATSimplePermissions(cmd
->subtype
, rel
, ATT_TABLE
);
5116 /* No command-specific prep needed */
5117 pass
= AT_PASS_MISC
;
5120 elog(ERROR
, "unrecognized alter table type: %d",
5121 (int) cmd
->subtype
);
5122 pass
= AT_PASS_UNSET
; /* keep compiler quiet */
5125 Assert(pass
> AT_PASS_UNSET
);
5127 /* Add the subcommand to the appropriate list for phase 2 */
5128 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd
);
5134 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5135 * dispatched in a "safe" execution order (designed to avoid unnecessary
5139 ATRewriteCatalogs(List
**wqueue
, LOCKMODE lockmode
,
5140 AlterTableUtilityContext
*context
)
5145 * We process all the tables "in parallel", one pass at a time. This is
5146 * needed because we may have to propagate work from one table to another
5147 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5148 * re-adding of the foreign key constraint to the other table). Work can
5149 * only be propagated into later passes, however.
5151 for (AlterTablePass pass
= 0; pass
< AT_NUM_PASSES
; pass
++)
5153 /* Go through each table that needs to be processed */
5154 foreach(ltab
, *wqueue
)
5156 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5157 List
*subcmds
= tab
->subcmds
[pass
];
5164 * Open the relation and store it in tab. This allows subroutines
5165 * close and reopen, if necessary. Appropriate lock was obtained
5166 * by phase 1, needn't get it again.
5168 tab
->rel
= relation_open(tab
->relid
, NoLock
);
5170 foreach(lcmd
, subcmds
)
5171 ATExecCmd(wqueue
, tab
,
5172 lfirst_node(AlterTableCmd
, lcmd
),
5173 lockmode
, pass
, context
);
5176 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5177 * (this is not done in ATExecAlterColumnType since it should be
5178 * done only once if multiple columns of a table are altered).
5180 if (pass
== AT_PASS_ALTER_TYPE
|| pass
== AT_PASS_SET_EXPRESSION
)
5181 ATPostAlterTypeCleanup(wqueue
, tab
, lockmode
);
5185 relation_close(tab
->rel
, NoLock
);
5191 /* Check to see if a toast table must be added. */
5192 foreach(ltab
, *wqueue
)
5194 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5197 * If the table is source table of ATTACH PARTITION command, we did
5198 * not modify anything about it that will change its toasting
5199 * requirement, so no need to check.
5201 if (((tab
->relkind
== RELKIND_RELATION
||
5202 tab
->relkind
== RELKIND_PARTITIONED_TABLE
) &&
5203 tab
->partition_constraint
== NULL
) ||
5204 tab
->relkind
== RELKIND_MATVIEW
)
5205 AlterTableCreateToastTable(tab
->relid
, (Datum
) 0, lockmode
);
5210 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5213 ATExecCmd(List
**wqueue
, AlteredTableInfo
*tab
,
5214 AlterTableCmd
*cmd
, LOCKMODE lockmode
, AlterTablePass cur_pass
,
5215 AlterTableUtilityContext
*context
)
5217 ObjectAddress address
= InvalidObjectAddress
;
5218 Relation rel
= tab
->rel
;
5220 switch (cmd
->subtype
)
5222 case AT_AddColumn
: /* ADD COLUMN */
5223 case AT_AddColumnToView
: /* add column via CREATE OR REPLACE VIEW */
5224 address
= ATExecAddColumn(wqueue
, tab
, rel
, &cmd
,
5225 cmd
->recurse
, false,
5226 lockmode
, cur_pass
, context
);
5228 case AT_ColumnDefault
: /* ALTER COLUMN DEFAULT */
5229 address
= ATExecColumnDefault(rel
, cmd
->name
, cmd
->def
, lockmode
);
5231 case AT_CookedColumnDefault
: /* add a pre-cooked default */
5232 address
= ATExecCookedColumnDefault(rel
, cmd
->num
, cmd
->def
);
5234 case AT_AddIdentity
:
5235 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5237 Assert(cmd
!= NULL
);
5238 address
= ATExecAddIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5240 case AT_SetIdentity
:
5241 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5243 Assert(cmd
!= NULL
);
5244 address
= ATExecSetIdentity(rel
, cmd
->name
, cmd
->def
, lockmode
, cmd
->recurse
, false);
5246 case AT_DropIdentity
:
5247 address
= ATExecDropIdentity(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
, cmd
->recurse
, false);
5249 case AT_DropNotNull
: /* ALTER COLUMN DROP NOT NULL */
5250 address
= ATExecDropNotNull(rel
, cmd
->name
, lockmode
);
5252 case AT_SetNotNull
: /* ALTER COLUMN SET NOT NULL */
5253 address
= ATExecSetNotNull(tab
, rel
, cmd
->name
, lockmode
);
5255 case AT_CheckNotNull
: /* check column is already marked NOT NULL */
5256 ATExecCheckNotNull(tab
, rel
, cmd
->name
, lockmode
);
5258 case AT_SetExpression
:
5259 address
= ATExecSetExpression(tab
, rel
, cmd
->name
, cmd
->def
, lockmode
);
5261 case AT_DropExpression
:
5262 address
= ATExecDropExpression(rel
, cmd
->name
, cmd
->missing_ok
, lockmode
);
5264 case AT_SetStatistics
: /* ALTER COLUMN SET STATISTICS */
5265 address
= ATExecSetStatistics(rel
, cmd
->name
, cmd
->num
, cmd
->def
, lockmode
);
5267 case AT_SetOptions
: /* ALTER COLUMN SET ( options ) */
5268 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, false, lockmode
);
5270 case AT_ResetOptions
: /* ALTER COLUMN RESET ( options ) */
5271 address
= ATExecSetOptions(rel
, cmd
->name
, cmd
->def
, true, lockmode
);
5273 case AT_SetStorage
: /* ALTER COLUMN SET STORAGE */
5274 address
= ATExecSetStorage(rel
, cmd
->name
, cmd
->def
, lockmode
);
5276 case AT_SetCompression
: /* ALTER COLUMN SET COMPRESSION */
5277 address
= ATExecSetCompression(rel
, cmd
->name
, cmd
->def
,
5280 case AT_DropColumn
: /* DROP COLUMN */
5281 address
= ATExecDropColumn(wqueue
, rel
, cmd
->name
,
5282 cmd
->behavior
, cmd
->recurse
, false,
5283 cmd
->missing_ok
, lockmode
,
5286 case AT_AddIndex
: /* ADD INDEX */
5287 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, false,
5290 case AT_ReAddIndex
: /* ADD INDEX */
5291 address
= ATExecAddIndex(tab
, rel
, (IndexStmt
*) cmd
->def
, true,
5294 case AT_ReAddStatistics
: /* ADD STATISTICS */
5295 address
= ATExecAddStatistics(tab
, rel
, (CreateStatsStmt
*) cmd
->def
,
5298 case AT_AddConstraint
: /* ADD CONSTRAINT */
5299 /* Transform the command only during initial examination */
5300 if (cur_pass
== AT_PASS_ADD_CONSTR
)
5301 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
,
5302 cmd
->recurse
, lockmode
,
5304 /* Depending on constraint type, might be no more work to do now */
5307 ATExecAddConstraint(wqueue
, tab
, rel
,
5308 (Constraint
*) cmd
->def
,
5309 cmd
->recurse
, false, lockmode
);
5311 case AT_ReAddConstraint
: /* Re-add pre-existing check constraint */
5313 ATExecAddConstraint(wqueue
, tab
, rel
, (Constraint
*) cmd
->def
,
5314 true, true, lockmode
);
5316 case AT_ReAddDomainConstraint
: /* Re-add pre-existing domain check
5319 AlterDomainAddConstraint(((AlterDomainStmt
*) cmd
->def
)->typeName
,
5320 ((AlterDomainStmt
*) cmd
->def
)->def
,
5323 case AT_ReAddComment
: /* Re-add existing comment */
5324 address
= CommentObject((CommentStmt
*) cmd
->def
);
5326 case AT_AddIndexConstraint
: /* ADD CONSTRAINT USING INDEX */
5327 address
= ATExecAddIndexConstraint(tab
, rel
, (IndexStmt
*) cmd
->def
,
5330 case AT_AlterConstraint
: /* ALTER CONSTRAINT */
5331 address
= ATExecAlterConstraint(rel
, cmd
, false, false, lockmode
);
5333 case AT_ValidateConstraint
: /* VALIDATE CONSTRAINT */
5334 address
= ATExecValidateConstraint(wqueue
, rel
, cmd
->name
, cmd
->recurse
,
5337 case AT_DropConstraint
: /* DROP CONSTRAINT */
5338 ATExecDropConstraint(rel
, cmd
->name
, cmd
->behavior
,
5339 cmd
->recurse
, false,
5340 cmd
->missing_ok
, lockmode
);
5342 case AT_AlterColumnType
: /* ALTER COLUMN TYPE */
5343 /* parse transformation was done earlier */
5344 address
= ATExecAlterColumnType(tab
, rel
, cmd
, lockmode
);
5346 case AT_AlterColumnGenericOptions
: /* ALTER COLUMN OPTIONS */
5348 ATExecAlterColumnGenericOptions(rel
, cmd
->name
,
5349 (List
*) cmd
->def
, lockmode
);
5351 case AT_ChangeOwner
: /* ALTER OWNER */
5352 ATExecChangeOwner(RelationGetRelid(rel
),
5353 get_rolespec_oid(cmd
->newowner
, false),
5356 case AT_ClusterOn
: /* CLUSTER ON */
5357 address
= ATExecClusterOn(rel
, cmd
->name
, lockmode
);
5359 case AT_DropCluster
: /* SET WITHOUT CLUSTER */
5360 ATExecDropCluster(rel
, lockmode
);
5362 case AT_SetLogged
: /* SET LOGGED */
5363 case AT_SetUnLogged
: /* SET UNLOGGED */
5365 case AT_DropOids
: /* SET WITHOUT OIDS */
5366 /* nothing to do here, oid columns don't exist anymore */
5368 case AT_SetAccessMethod
: /* SET ACCESS METHOD */
5371 * Only do this for partitioned tables, for which this is just a
5372 * catalog change. Tables with storage are handled by Phase 3.
5374 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
5375 tab
->chgAccessMethod
)
5376 ATExecSetAccessMethodNoStorage(rel
, tab
->newAccessMethod
);
5378 case AT_SetTableSpace
: /* SET TABLESPACE */
5381 * Only do this for partitioned tables and indexes, for which this
5382 * is just a catalog change. Other relation types which have
5383 * storage are handled by Phase 3.
5385 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
5386 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
5387 ATExecSetTableSpaceNoStorage(rel
, tab
->newTableSpace
);
5390 case AT_SetRelOptions
: /* SET (...) */
5391 case AT_ResetRelOptions
: /* RESET (...) */
5392 case AT_ReplaceRelOptions
: /* replace entire option list */
5393 ATExecSetRelOptions(rel
, (List
*) cmd
->def
, cmd
->subtype
, lockmode
);
5395 case AT_EnableTrig
: /* ENABLE TRIGGER name */
5396 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5397 TRIGGER_FIRES_ON_ORIGIN
, false,
5401 case AT_EnableAlwaysTrig
: /* ENABLE ALWAYS TRIGGER name */
5402 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5403 TRIGGER_FIRES_ALWAYS
, false,
5407 case AT_EnableReplicaTrig
: /* ENABLE REPLICA TRIGGER name */
5408 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5409 TRIGGER_FIRES_ON_REPLICA
, false,
5413 case AT_DisableTrig
: /* DISABLE TRIGGER name */
5414 ATExecEnableDisableTrigger(rel
, cmd
->name
,
5415 TRIGGER_DISABLED
, false,
5419 case AT_EnableTrigAll
: /* ENABLE TRIGGER ALL */
5420 ATExecEnableDisableTrigger(rel
, NULL
,
5421 TRIGGER_FIRES_ON_ORIGIN
, false,
5425 case AT_DisableTrigAll
: /* DISABLE TRIGGER ALL */
5426 ATExecEnableDisableTrigger(rel
, NULL
,
5427 TRIGGER_DISABLED
, false,
5431 case AT_EnableTrigUser
: /* ENABLE TRIGGER USER */
5432 ATExecEnableDisableTrigger(rel
, NULL
,
5433 TRIGGER_FIRES_ON_ORIGIN
, true,
5437 case AT_DisableTrigUser
: /* DISABLE TRIGGER USER */
5438 ATExecEnableDisableTrigger(rel
, NULL
,
5439 TRIGGER_DISABLED
, true,
5444 case AT_EnableRule
: /* ENABLE RULE name */
5445 ATExecEnableDisableRule(rel
, cmd
->name
,
5446 RULE_FIRES_ON_ORIGIN
, lockmode
);
5448 case AT_EnableAlwaysRule
: /* ENABLE ALWAYS RULE name */
5449 ATExecEnableDisableRule(rel
, cmd
->name
,
5450 RULE_FIRES_ALWAYS
, lockmode
);
5452 case AT_EnableReplicaRule
: /* ENABLE REPLICA RULE name */
5453 ATExecEnableDisableRule(rel
, cmd
->name
,
5454 RULE_FIRES_ON_REPLICA
, lockmode
);
5456 case AT_DisableRule
: /* DISABLE RULE name */
5457 ATExecEnableDisableRule(rel
, cmd
->name
,
5458 RULE_DISABLED
, lockmode
);
5462 address
= ATExecAddInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5464 case AT_DropInherit
:
5465 address
= ATExecDropInherit(rel
, (RangeVar
*) cmd
->def
, lockmode
);
5468 address
= ATExecAddOf(rel
, (TypeName
*) cmd
->def
, lockmode
);
5471 ATExecDropOf(rel
, lockmode
);
5473 case AT_ReplicaIdentity
:
5474 ATExecReplicaIdentity(rel
, (ReplicaIdentityStmt
*) cmd
->def
, lockmode
);
5476 case AT_EnableRowSecurity
:
5477 ATExecSetRowSecurity(rel
, true);
5479 case AT_DisableRowSecurity
:
5480 ATExecSetRowSecurity(rel
, false);
5482 case AT_ForceRowSecurity
:
5483 ATExecForceNoForceRowSecurity(rel
, true);
5485 case AT_NoForceRowSecurity
:
5486 ATExecForceNoForceRowSecurity(rel
, false);
5488 case AT_GenericOptions
:
5489 ATExecGenericOptions(rel
, (List
*) cmd
->def
);
5491 case AT_AttachPartition
:
5492 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5494 Assert(cmd
!= NULL
);
5495 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
5496 address
= ATExecAttachPartition(wqueue
, rel
, (PartitionCmd
*) cmd
->def
,
5499 address
= ATExecAttachPartitionIdx(wqueue
, rel
,
5500 ((PartitionCmd
*) cmd
->def
)->name
);
5502 case AT_DetachPartition
:
5503 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5505 Assert(cmd
!= NULL
);
5506 /* ATPrepCmd ensures it must be a table */
5507 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5508 address
= ATExecDetachPartition(wqueue
, tab
, rel
,
5509 ((PartitionCmd
*) cmd
->def
)->name
,
5510 ((PartitionCmd
*) cmd
->def
)->concurrent
);
5512 case AT_DetachPartitionFinalize
:
5513 address
= ATExecDetachPartitionFinalize(rel
, ((PartitionCmd
*) cmd
->def
)->name
);
5515 case AT_SplitPartition
:
5516 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5518 Assert(cmd
!= NULL
);
5519 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5520 ATExecSplitPartition(wqueue
, tab
, rel
, (PartitionCmd
*) cmd
->def
,
5523 case AT_MergePartitions
:
5524 cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, cmd
, false, lockmode
,
5526 Assert(cmd
!= NULL
);
5527 Assert(rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
5528 ATExecMergePartitions(wqueue
, tab
, rel
, (PartitionCmd
*) cmd
->def
,
5532 elog(ERROR
, "unrecognized alter table type: %d",
5533 (int) cmd
->subtype
);
5538 * Report the subcommand to interested event triggers.
5541 EventTriggerCollectAlterTableSubcmd((Node
*) cmd
, address
);
5544 * Bump the command counter to ensure the next subcommand in the sequence
5545 * can see the changes so far
5547 CommandCounterIncrement();
5551 * ATParseTransformCmd: perform parse transformation for one subcommand
5553 * Returns the transformed subcommand tree, if there is one, else NULL.
5555 * The parser may hand back additional AlterTableCmd(s) and/or other
5556 * utility statements, either before or after the original subcommand.
5557 * Other AlterTableCmds are scheduled into the appropriate slot of the
5558 * AlteredTableInfo (they had better be for later passes than the current one).
5559 * Utility statements that are supposed to happen before the AlterTableCmd
5560 * are executed immediately. Those that are supposed to happen afterwards
5561 * are added to the tab->afterStmts list to be done at the very end.
5563 static AlterTableCmd
*
5564 ATParseTransformCmd(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
5565 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
5566 AlterTablePass cur_pass
, AlterTableUtilityContext
*context
)
5568 AlterTableCmd
*newcmd
= NULL
;
5569 AlterTableStmt
*atstmt
= makeNode(AlterTableStmt
);
5574 /* Gin up an AlterTableStmt with just this subcommand and this table */
5576 makeRangeVar(get_namespace_name(RelationGetNamespace(rel
)),
5577 pstrdup(RelationGetRelationName(rel
)),
5579 atstmt
->relation
->inh
= recurse
;
5580 atstmt
->cmds
= list_make1(cmd
);
5581 atstmt
->objtype
= OBJECT_TABLE
; /* needn't be picky here */
5582 atstmt
->missing_ok
= false;
5584 /* Transform the AlterTableStmt */
5585 atstmt
= transformAlterTableStmt(RelationGetRelid(rel
),
5587 context
->queryString
,
5591 /* Execute any statements that should happen before these subcommand(s) */
5592 foreach(lc
, beforeStmts
)
5594 Node
*stmt
= (Node
*) lfirst(lc
);
5596 ProcessUtilityForAlterTable(stmt
, context
);
5597 CommandCounterIncrement();
5600 /* Examine the transformed subcommands and schedule them appropriately */
5601 foreach(lc
, atstmt
->cmds
)
5603 AlterTableCmd
*cmd2
= lfirst_node(AlterTableCmd
, lc
);
5604 AlterTablePass pass
;
5607 * This switch need only cover the subcommand types that can be added
5608 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5609 * executing the subcommand immediately, as a substitute for the
5610 * original subcommand. (Note, however, that this does cause
5611 * AT_AddConstraint subcommands to be rescheduled into later passes,
5612 * which is important for index and foreign key constraints.)
5614 * We assume we needn't do any phase-1 checks for added subcommands.
5616 switch (cmd2
->subtype
)
5619 /* Need command-specific recursion decision */
5620 ATPrepSetNotNull(wqueue
, rel
, cmd2
,
5623 pass
= AT_PASS_COL_ATTRS
;
5626 /* This command never recurses */
5627 /* No command-specific prep needed */
5628 pass
= AT_PASS_ADD_INDEX
;
5630 case AT_AddIndexConstraint
:
5631 /* This command never recurses */
5632 /* No command-specific prep needed */
5633 pass
= AT_PASS_ADD_INDEXCONSTR
;
5635 case AT_AddConstraint
:
5636 /* Recursion occurs during execution phase */
5638 cmd2
->recurse
= true;
5639 switch (castNode(Constraint
, cmd2
->def
)->contype
)
5641 case CONSTR_PRIMARY
:
5643 case CONSTR_EXCLUSION
:
5644 pass
= AT_PASS_ADD_INDEXCONSTR
;
5647 pass
= AT_PASS_ADD_OTHERCONSTR
;
5651 case AT_AlterColumnGenericOptions
:
5652 /* This command never recurses */
5653 /* No command-specific prep needed */
5654 pass
= AT_PASS_MISC
;
5661 if (pass
< cur_pass
)
5663 /* Cannot schedule into a pass we already finished */
5664 elog(ERROR
, "ALTER TABLE scheduling failure: too late for pass %d",
5667 else if (pass
> cur_pass
)
5669 /* OK, queue it up for later */
5670 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], cmd2
);
5675 * We should see at most one subcommand for the current pass,
5676 * which is the transformed version of the original subcommand.
5678 if (newcmd
== NULL
&& cmd
->subtype
== cmd2
->subtype
)
5680 /* Found the transformed version of our subcommand */
5684 elog(ERROR
, "ALTER TABLE scheduling failure: bogus item for pass %d",
5689 /* Queue up any after-statements to happen at the end */
5690 tab
->afterStmts
= list_concat(tab
->afterStmts
, afterStmts
);
5696 * ATRewriteTables: ALTER TABLE phase 3
5699 ATRewriteTables(AlterTableStmt
*parsetree
, List
**wqueue
, LOCKMODE lockmode
,
5700 AlterTableUtilityContext
*context
)
5704 /* Go through each table that needs to be checked or rewritten */
5705 foreach(ltab
, *wqueue
)
5707 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5709 /* Relations without storage may be ignored here */
5710 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5714 * If we change column data types, the operation has to be propagated
5715 * to tables that use this table's rowtype as a column type.
5716 * tab->newvals will also be non-NULL in the case where we're adding a
5717 * column with a default. We choose to forbid that case as well,
5718 * since composite types might eventually support defaults.
5720 * (Eventually we'll probably need to check for composite type
5721 * dependencies even when we're just scanning the table without a
5722 * rewrite, but at the moment a composite type does not enforce any
5723 * constraints, so it's not necessary/appropriate to enforce them just
5726 if (tab
->newvals
!= NIL
|| tab
->rewrite
> 0)
5730 rel
= table_open(tab
->relid
, NoLock
);
5731 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
5732 table_close(rel
, NoLock
);
5736 * We only need to rewrite the table if at least one column needs to
5737 * be recomputed, or we are changing its persistence or access method.
5739 * There are two reasons for requiring a rewrite when changing
5740 * persistence: on one hand, we need to ensure that the buffers
5741 * belonging to each of the two relations are marked with or without
5742 * BM_PERMANENT properly. On the other hand, since rewriting creates
5743 * and assigns a new relfilenumber, we automatically create or drop an
5744 * init fork for the relation as appropriate.
5746 if (tab
->rewrite
> 0 && tab
->relkind
!= RELKIND_SEQUENCE
)
5748 /* Build a temporary relation and copy data */
5751 Oid NewAccessMethod
;
5755 OldHeap
= table_open(tab
->relid
, NoLock
);
5758 * We don't support rewriting of system catalogs; there are too
5759 * many corner cases and too little benefit. In particular this
5760 * is certainly not going to work for mapped catalogs.
5762 if (IsSystemRelation(OldHeap
))
5764 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5765 errmsg("cannot rewrite system relation \"%s\"",
5766 RelationGetRelationName(OldHeap
))));
5768 if (RelationIsUsedAsCatalogTable(OldHeap
))
5770 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5771 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5772 RelationGetRelationName(OldHeap
))));
5775 * Don't allow rewrite on temp tables of other backends ... their
5776 * local buffer manager is not going to cope. (This is redundant
5777 * with the check in CheckAlterTableIsSafe, but for safety we'll
5780 if (RELATION_IS_OTHER_TEMP(OldHeap
))
5782 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5783 errmsg("cannot rewrite temporary tables of other sessions")));
5786 * Select destination tablespace (same as original unless user
5787 * requested a change)
5789 if (tab
->newTableSpace
)
5790 NewTableSpace
= tab
->newTableSpace
;
5792 NewTableSpace
= OldHeap
->rd_rel
->reltablespace
;
5795 * Select destination access method (same as original unless user
5796 * requested a change)
5798 if (tab
->chgAccessMethod
)
5799 NewAccessMethod
= tab
->newAccessMethod
;
5801 NewAccessMethod
= OldHeap
->rd_rel
->relam
;
5804 * Select persistence of transient table (same as original unless
5805 * user requested a change)
5807 persistence
= tab
->chgPersistence
?
5808 tab
->newrelpersistence
: OldHeap
->rd_rel
->relpersistence
;
5810 table_close(OldHeap
, NoLock
);
5813 * Fire off an Event Trigger now, before actually rewriting the
5816 * We don't support Event Trigger for nested commands anywhere,
5817 * here included, and parsetree is given NULL when coming from
5818 * AlterTableInternal.
5820 * And fire it only once.
5823 EventTriggerTableRewrite((Node
*) parsetree
,
5828 * Create transient table that will receive the modified data.
5830 * Ensure it is marked correctly as logged or unlogged. We have
5831 * to do this here so that buffers for the new relfilenumber will
5832 * have the right persistence set, and at the same time ensure
5833 * that the original filenumbers's buffers will get read in with
5834 * the correct setting (i.e. the original one). Otherwise a
5835 * rollback after the rewrite would possibly result with buffers
5836 * for the original filenumbers having the wrong persistence
5839 * NB: This relies on swap_relation_files() also swapping the
5840 * persistence. That wouldn't work for pg_class, but that can't be
5843 OIDNewHeap
= make_new_heap(tab
->relid
, NewTableSpace
, NewAccessMethod
,
5844 persistence
, lockmode
);
5847 * Copy the heap data into the new table with the desired
5848 * modifications, and test the current data within the table
5849 * against new constraints generated by ALTER TABLE commands.
5851 ATRewriteTable(tab
, OIDNewHeap
, lockmode
);
5854 * Swap the physical files of the old and new heaps, then rebuild
5855 * indexes and discard the old heap. We can use RecentXmin for
5856 * the table's new relfrozenxid because we rewrote all the tuples
5857 * in ATRewriteTable, so no older Xid remains in the table. Also,
5858 * we never try to swap toast tables by content, since we have no
5859 * interest in letting this code work on system catalogs.
5861 finish_heap_swap(tab
->relid
, OIDNewHeap
,
5863 !OidIsValid(tab
->newTableSpace
),
5865 ReadNextMultiXactId(),
5868 InvokeObjectPostAlterHook(RelationRelationId
, tab
->relid
, 0);
5870 else if (tab
->rewrite
> 0 && tab
->relkind
== RELKIND_SEQUENCE
)
5872 if (tab
->chgPersistence
)
5873 SequenceChangePersistence(tab
->relid
, tab
->newrelpersistence
);
5878 * If required, test the current data within the table against new
5879 * constraints generated by ALTER TABLE commands, but don't
5882 if (tab
->constraints
!= NIL
|| tab
->verify_new_notnull
||
5883 tab
->partition_constraint
!= NULL
)
5884 ATRewriteTable(tab
, InvalidOid
, lockmode
);
5887 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5888 * just do a block-by-block copy.
5890 if (tab
->newTableSpace
)
5891 ATExecSetTableSpace(tab
->relid
, tab
->newTableSpace
, lockmode
);
5895 * Also change persistence of owned sequences, so that it matches the
5896 * table persistence.
5898 if (tab
->chgPersistence
)
5900 List
*seqlist
= getOwnedSequences(tab
->relid
);
5903 foreach(lc
, seqlist
)
5905 Oid seq_relid
= lfirst_oid(lc
);
5907 SequenceChangePersistence(seq_relid
, tab
->newrelpersistence
);
5913 * Foreign key constraints are checked in a final pass, since (a) it's
5914 * generally best to examine each one separately, and (b) it's at least
5915 * theoretically possible that we have changed both relations of the
5916 * foreign key, and we'd better have finished both rewrites before we try
5917 * to read the tables.
5919 foreach(ltab
, *wqueue
)
5921 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5922 Relation rel
= NULL
;
5925 /* Relations without storage may be ignored here too */
5926 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
5929 foreach(lcon
, tab
->constraints
)
5931 NewConstraint
*con
= lfirst(lcon
);
5933 if (con
->contype
== CONSTR_FOREIGN
)
5935 Constraint
*fkconstraint
= (Constraint
*) con
->qual
;
5940 /* Long since locked, no need for another */
5941 rel
= table_open(tab
->relid
, NoLock
);
5944 refrel
= table_open(con
->refrelid
, RowShareLock
);
5946 validateForeignKeyConstraint(fkconstraint
->conname
, rel
, refrel
,
5951 * No need to mark the constraint row as validated, we did
5952 * that when we inserted the row earlier.
5955 table_close(refrel
, NoLock
);
5960 table_close(rel
, NoLock
);
5963 /* Finally, run any afterStmts that were queued up */
5964 foreach(ltab
, *wqueue
)
5966 AlteredTableInfo
*tab
= (AlteredTableInfo
*) lfirst(ltab
);
5969 foreach(lc
, tab
->afterStmts
)
5971 Node
*stmt
= (Node
*) lfirst(lc
);
5973 ProcessUtilityForAlterTable(stmt
, context
);
5974 CommandCounterIncrement();
5980 * ATRewriteTable: scan or rewrite one table
5982 * OIDNewHeap is InvalidOid if we don't need to rewrite
5985 ATRewriteTable(AlteredTableInfo
*tab
, Oid OIDNewHeap
, LOCKMODE lockmode
)
5989 TupleDesc oldTupDesc
;
5990 TupleDesc newTupDesc
;
5991 bool needscan
= false;
5992 List
*notnull_attrs
;
5997 BulkInsertState bistate
;
5999 ExprState
*partqualstate
= NULL
;
6002 * Open the relation(s). We have surely already locked the existing
6005 oldrel
= table_open(tab
->relid
, NoLock
);
6006 oldTupDesc
= tab
->oldDesc
;
6007 newTupDesc
= RelationGetDescr(oldrel
); /* includes all mods */
6009 if (OidIsValid(OIDNewHeap
))
6010 newrel
= table_open(OIDNewHeap
, lockmode
);
6015 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6016 * is empty, so don't bother using it.
6020 mycid
= GetCurrentCommandId(true);
6021 bistate
= GetBulkInsertState();
6022 ti_options
= TABLE_INSERT_SKIP_FSM
;
6026 /* keep compiler quiet about using these uninitialized */
6033 * Generate the constraint and default execution states
6036 estate
= CreateExecutorState();
6038 /* Build the needed expression execution states */
6039 foreach(l
, tab
->constraints
)
6041 NewConstraint
*con
= lfirst(l
);
6043 switch (con
->contype
)
6047 con
->qualstate
= ExecPrepareExpr((Expr
*) con
->qual
, estate
);
6049 case CONSTR_FOREIGN
:
6050 /* Nothing to do here */
6053 elog(ERROR
, "unrecognized constraint type: %d",
6054 (int) con
->contype
);
6058 /* Build expression execution states for partition check quals */
6059 if (tab
->partition_constraint
)
6062 partqualstate
= ExecPrepareExpr(tab
->partition_constraint
, estate
);
6065 foreach(l
, tab
->newvals
)
6067 NewColumnValue
*ex
= lfirst(l
);
6069 /* expr already planned */
6070 ex
->exprstate
= ExecInitExpr((Expr
*) ex
->expr
, NULL
);
6073 notnull_attrs
= NIL
;
6074 if (newrel
|| tab
->verify_new_notnull
)
6077 * If we are rebuilding the tuples OR if we added any new but not
6078 * verified not-null constraints, check all not-null constraints. This
6079 * is a bit of overkill but it minimizes risk of bugs, and
6080 * heap_attisnull is a pretty cheap test anyway.
6082 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6084 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, i
);
6086 if (attr
->attnotnull
&& !attr
->attisdropped
)
6087 notnull_attrs
= lappend_int(notnull_attrs
, i
);
6093 if (newrel
|| needscan
)
6095 ExprContext
*econtext
;
6096 TupleTableSlot
*oldslot
;
6097 TupleTableSlot
*newslot
;
6099 MemoryContext oldCxt
;
6100 List
*dropped_attrs
= NIL
;
6106 (errmsg_internal("rewriting table \"%s\"",
6107 RelationGetRelationName(oldrel
))));
6110 (errmsg_internal("verifying table \"%s\"",
6111 RelationGetRelationName(oldrel
))));
6116 * All predicate locks on the tuples or pages are about to be made
6117 * invalid, because we move tuples around. Promote them to
6120 TransferPredicateLocksToHeapRelation(oldrel
);
6123 econtext
= GetPerTupleExprContext(estate
);
6126 * Create necessary tuple slots. When rewriting, two slots are needed,
6127 * otherwise one suffices. In the case where one slot suffices, we
6128 * need to use the new tuple descriptor, otherwise some constraints
6129 * can't be evaluated. Note that even when the tuple layout is the
6130 * same and no rewrite is required, the tupDescs might not be
6131 * (consider ADD COLUMN without a default).
6135 Assert(newrel
!= NULL
);
6136 oldslot
= MakeSingleTupleTableSlot(oldTupDesc
,
6137 table_slot_callbacks(oldrel
));
6138 newslot
= MakeSingleTupleTableSlot(newTupDesc
,
6139 table_slot_callbacks(newrel
));
6142 * Set all columns in the new slot to NULL initially, to ensure
6143 * columns added as part of the rewrite are initialized to NULL.
6144 * That is necessary as tab->newvals will not contain an
6145 * expression for columns with a NULL default, e.g. when adding a
6146 * column without a default together with a column with a default
6147 * requiring an actual rewrite.
6149 ExecStoreAllNullTuple(newslot
);
6153 oldslot
= MakeSingleTupleTableSlot(newTupDesc
,
6154 table_slot_callbacks(oldrel
));
6159 * Any attributes that are dropped according to the new tuple
6160 * descriptor can be set to NULL. We precompute the list of dropped
6161 * attributes to avoid needing to do so in the per-tuple loop.
6163 for (i
= 0; i
< newTupDesc
->natts
; i
++)
6165 if (TupleDescAttr(newTupDesc
, i
)->attisdropped
)
6166 dropped_attrs
= lappend_int(dropped_attrs
, i
);
6170 * Scan through the rows, generating a new row if needed and then
6171 * checking all the constraints.
6173 snapshot
= RegisterSnapshot(GetLatestSnapshot());
6174 scan
= table_beginscan(oldrel
, snapshot
, 0, NULL
);
6177 * Switch to per-tuple memory context and reset it for each tuple
6178 * produced, so we don't leak memory.
6180 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
6182 while (table_scan_getnextslot(scan
, ForwardScanDirection
, oldslot
))
6184 TupleTableSlot
*insertslot
;
6186 if (tab
->rewrite
> 0)
6188 /* Extract data from old tuple */
6189 slot_getallattrs(oldslot
);
6190 ExecClearTuple(newslot
);
6192 /* copy attributes */
6193 memcpy(newslot
->tts_values
, oldslot
->tts_values
,
6194 sizeof(Datum
) * oldslot
->tts_nvalid
);
6195 memcpy(newslot
->tts_isnull
, oldslot
->tts_isnull
,
6196 sizeof(bool) * oldslot
->tts_nvalid
);
6198 /* Set dropped attributes to null in new tuple */
6199 foreach(lc
, dropped_attrs
)
6200 newslot
->tts_isnull
[lfirst_int(lc
)] = true;
6203 * Constraints and GENERATED expressions might reference the
6204 * tableoid column, so fill tts_tableOid with the desired
6205 * value. (We must do this each time, because it gets
6206 * overwritten with newrel's OID during storing.)
6208 newslot
->tts_tableOid
= RelationGetRelid(oldrel
);
6211 * Process supplied expressions to replace selected columns.
6213 * First, evaluate expressions whose inputs come from the old
6216 econtext
->ecxt_scantuple
= oldslot
;
6218 foreach(l
, tab
->newvals
)
6220 NewColumnValue
*ex
= lfirst(l
);
6222 if (ex
->is_generated
)
6225 newslot
->tts_values
[ex
->attnum
- 1]
6226 = ExecEvalExpr(ex
->exprstate
,
6228 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6231 ExecStoreVirtualTuple(newslot
);
6234 * Now, evaluate any expressions whose inputs come from the
6235 * new tuple. We assume these columns won't reference each
6236 * other, so that there's no ordering dependency.
6238 econtext
->ecxt_scantuple
= newslot
;
6240 foreach(l
, tab
->newvals
)
6242 NewColumnValue
*ex
= lfirst(l
);
6244 if (!ex
->is_generated
)
6247 newslot
->tts_values
[ex
->attnum
- 1]
6248 = ExecEvalExpr(ex
->exprstate
,
6250 &newslot
->tts_isnull
[ex
->attnum
- 1]);
6253 insertslot
= newslot
;
6258 * If there's no rewrite, old and new table are guaranteed to
6259 * have the same AM, so we can just use the old slot to verify
6260 * new constraints etc.
6262 insertslot
= oldslot
;
6265 /* Now check any constraints on the possibly-changed tuple */
6266 econtext
->ecxt_scantuple
= insertslot
;
6268 foreach(l
, notnull_attrs
)
6270 int attn
= lfirst_int(l
);
6272 if (slot_attisnull(insertslot
, attn
+ 1))
6274 Form_pg_attribute attr
= TupleDescAttr(newTupDesc
, attn
);
6277 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
6278 errmsg("column \"%s\" of relation \"%s\" contains null values",
6279 NameStr(attr
->attname
),
6280 RelationGetRelationName(oldrel
)),
6281 errtablecol(oldrel
, attn
+ 1)));
6285 foreach(l
, tab
->constraints
)
6287 NewConstraint
*con
= lfirst(l
);
6289 switch (con
->contype
)
6292 if (!ExecCheck(con
->qualstate
, econtext
))
6294 (errcode(ERRCODE_CHECK_VIOLATION
),
6295 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6297 RelationGetRelationName(oldrel
)),
6298 errtableconstraint(oldrel
, con
->name
)));
6300 case CONSTR_FOREIGN
:
6301 /* Nothing to do here */
6304 elog(ERROR
, "unrecognized constraint type: %d",
6305 (int) con
->contype
);
6309 if (partqualstate
&& !ExecCheck(partqualstate
, econtext
))
6311 if (tab
->validate_default
)
6313 (errcode(ERRCODE_CHECK_VIOLATION
),
6314 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6315 RelationGetRelationName(oldrel
)),
6319 (errcode(ERRCODE_CHECK_VIOLATION
),
6320 errmsg("partition constraint of relation \"%s\" is violated by some row",
6321 RelationGetRelationName(oldrel
)),
6325 /* Write the tuple out to the new relation */
6327 table_tuple_insert(newrel
, insertslot
, mycid
,
6328 ti_options
, bistate
);
6330 ResetExprContext(econtext
);
6332 CHECK_FOR_INTERRUPTS();
6335 MemoryContextSwitchTo(oldCxt
);
6336 table_endscan(scan
);
6337 UnregisterSnapshot(snapshot
);
6339 ExecDropSingleTupleTableSlot(oldslot
);
6341 ExecDropSingleTupleTableSlot(newslot
);
6344 FreeExecutorState(estate
);
6346 table_close(oldrel
, NoLock
);
6349 FreeBulkInsertState(bistate
);
6351 table_finish_bulk_insert(newrel
, ti_options
);
6353 table_close(newrel
, NoLock
);
6358 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6360 static AlteredTableInfo
*
6361 ATGetQueueEntry(List
**wqueue
, Relation rel
)
6363 Oid relid
= RelationGetRelid(rel
);
6364 AlteredTableInfo
*tab
;
6367 foreach(ltab
, *wqueue
)
6369 tab
= (AlteredTableInfo
*) lfirst(ltab
);
6370 if (tab
->relid
== relid
)
6375 * Not there, so add it. Note that we make a copy of the relation's
6376 * existing descriptor before anything interesting can happen to it.
6378 tab
= (AlteredTableInfo
*) palloc0(sizeof(AlteredTableInfo
));
6380 tab
->rel
= NULL
; /* set later */
6381 tab
->relkind
= rel
->rd_rel
->relkind
;
6382 tab
->oldDesc
= CreateTupleDescCopyConstr(RelationGetDescr(rel
));
6383 tab
->newAccessMethod
= InvalidOid
;
6384 tab
->chgAccessMethod
= false;
6385 tab
->newTableSpace
= InvalidOid
;
6386 tab
->newrelpersistence
= RELPERSISTENCE_PERMANENT
;
6387 tab
->chgPersistence
= false;
6389 *wqueue
= lappend(*wqueue
, tab
);
6395 alter_table_type_to_string(AlterTableType cmdtype
)
6400 case AT_AddColumnToView
:
6401 return "ADD COLUMN";
6402 case AT_ColumnDefault
:
6403 case AT_CookedColumnDefault
:
6404 return "ALTER COLUMN ... SET DEFAULT";
6405 case AT_DropNotNull
:
6406 return "ALTER COLUMN ... DROP NOT NULL";
6408 return "ALTER COLUMN ... SET NOT NULL";
6409 case AT_SetExpression
:
6410 return "ALTER COLUMN ... SET EXPRESSION";
6411 case AT_DropExpression
:
6412 return "ALTER COLUMN ... DROP EXPRESSION";
6413 case AT_CheckNotNull
:
6414 return NULL
; /* not real grammar */
6415 case AT_SetStatistics
:
6416 return "ALTER COLUMN ... SET STATISTICS";
6418 return "ALTER COLUMN ... SET";
6419 case AT_ResetOptions
:
6420 return "ALTER COLUMN ... RESET";
6422 return "ALTER COLUMN ... SET STORAGE";
6423 case AT_SetCompression
:
6424 return "ALTER COLUMN ... SET COMPRESSION";
6426 return "DROP COLUMN";
6429 return NULL
; /* not real grammar */
6430 case AT_AddConstraint
:
6431 case AT_ReAddConstraint
:
6432 case AT_ReAddDomainConstraint
:
6433 case AT_AddIndexConstraint
:
6434 return "ADD CONSTRAINT";
6435 case AT_AlterConstraint
:
6436 return "ALTER CONSTRAINT";
6437 case AT_ValidateConstraint
:
6438 return "VALIDATE CONSTRAINT";
6439 case AT_DropConstraint
:
6440 return "DROP CONSTRAINT";
6441 case AT_ReAddComment
:
6442 return NULL
; /* not real grammar */
6443 case AT_AlterColumnType
:
6444 return "ALTER COLUMN ... SET DATA TYPE";
6445 case AT_AlterColumnGenericOptions
:
6446 return "ALTER COLUMN ... OPTIONS";
6447 case AT_ChangeOwner
:
6450 return "CLUSTER ON";
6451 case AT_DropCluster
:
6452 return "SET WITHOUT CLUSTER";
6453 case AT_SetAccessMethod
:
6454 return "SET ACCESS METHOD";
6456 return "SET LOGGED";
6457 case AT_SetUnLogged
:
6458 return "SET UNLOGGED";
6460 return "SET WITHOUT OIDS";
6461 case AT_SetTableSpace
:
6462 return "SET TABLESPACE";
6463 case AT_SetRelOptions
:
6465 case AT_ResetRelOptions
:
6467 case AT_ReplaceRelOptions
:
6468 return NULL
; /* not real grammar */
6470 return "ENABLE TRIGGER";
6471 case AT_EnableAlwaysTrig
:
6472 return "ENABLE ALWAYS TRIGGER";
6473 case AT_EnableReplicaTrig
:
6474 return "ENABLE REPLICA TRIGGER";
6475 case AT_DisableTrig
:
6476 return "DISABLE TRIGGER";
6477 case AT_EnableTrigAll
:
6478 return "ENABLE TRIGGER ALL";
6479 case AT_DisableTrigAll
:
6480 return "DISABLE TRIGGER ALL";
6481 case AT_EnableTrigUser
:
6482 return "ENABLE TRIGGER USER";
6483 case AT_DisableTrigUser
:
6484 return "DISABLE TRIGGER USER";
6486 return "ENABLE RULE";
6487 case AT_EnableAlwaysRule
:
6488 return "ENABLE ALWAYS RULE";
6489 case AT_EnableReplicaRule
:
6490 return "ENABLE REPLICA RULE";
6491 case AT_DisableRule
:
6492 return "DISABLE RULE";
6495 case AT_DropInherit
:
6496 return "NO INHERIT";
6501 case AT_ReplicaIdentity
:
6502 return "REPLICA IDENTITY";
6503 case AT_EnableRowSecurity
:
6504 return "ENABLE ROW SECURITY";
6505 case AT_DisableRowSecurity
:
6506 return "DISABLE ROW SECURITY";
6507 case AT_ForceRowSecurity
:
6508 return "FORCE ROW SECURITY";
6509 case AT_NoForceRowSecurity
:
6510 return "NO FORCE ROW SECURITY";
6511 case AT_GenericOptions
:
6513 case AT_AttachPartition
:
6514 return "ATTACH PARTITION";
6515 case AT_DetachPartition
:
6516 return "DETACH PARTITION";
6517 case AT_DetachPartitionFinalize
:
6518 return "DETACH PARTITION ... FINALIZE";
6519 case AT_SplitPartition
:
6520 return "SPLIT PARTITION";
6521 case AT_MergePartitions
:
6522 return "MERGE PARTITIONS";
6523 case AT_AddIdentity
:
6524 return "ALTER COLUMN ... ADD IDENTITY";
6525 case AT_SetIdentity
:
6526 return "ALTER COLUMN ... SET";
6527 case AT_DropIdentity
:
6528 return "ALTER COLUMN ... DROP IDENTITY";
6529 case AT_ReAddStatistics
:
6530 return NULL
; /* not real grammar */
6537 * ATSimplePermissions
6539 * - Ensure that it is a relation (or possibly a view)
6540 * - Ensure this user is the owner
6541 * - Ensure that it is not a system table
6544 ATSimplePermissions(AlterTableType cmdtype
, Relation rel
, int allowed_targets
)
6548 switch (rel
->rd_rel
->relkind
)
6550 case RELKIND_RELATION
:
6551 case RELKIND_PARTITIONED_TABLE
:
6552 actual_target
= ATT_TABLE
;
6555 actual_target
= ATT_VIEW
;
6557 case RELKIND_MATVIEW
:
6558 actual_target
= ATT_MATVIEW
;
6561 actual_target
= ATT_INDEX
;
6563 case RELKIND_PARTITIONED_INDEX
:
6564 actual_target
= ATT_PARTITIONED_INDEX
;
6566 case RELKIND_COMPOSITE_TYPE
:
6567 actual_target
= ATT_COMPOSITE_TYPE
;
6569 case RELKIND_FOREIGN_TABLE
:
6570 actual_target
= ATT_FOREIGN_TABLE
;
6572 case RELKIND_SEQUENCE
:
6573 actual_target
= ATT_SEQUENCE
;
6580 /* Wrong target type? */
6581 if ((actual_target
& allowed_targets
) == 0)
6583 const char *action_str
= alter_table_type_to_string(cmdtype
);
6587 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6588 /* translator: %s is a group of some SQL keywords */
6589 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6590 action_str
, RelationGetRelationName(rel
)),
6591 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
6593 /* internal error? */
6594 elog(ERROR
, "invalid ALTER action attempted on relation \"%s\"",
6595 RelationGetRelationName(rel
));
6598 /* Permissions checks */
6599 if (!object_ownercheck(RelationRelationId
, RelationGetRelid(rel
), GetUserId()))
6600 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(rel
->rd_rel
->relkind
),
6601 RelationGetRelationName(rel
));
6603 if (!allowSystemTableMods
&& IsSystemRelation(rel
))
6605 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
6606 errmsg("permission denied: \"%s\" is a system catalog",
6607 RelationGetRelationName(rel
))));
6613 * Simple table recursion sufficient for most ALTER TABLE operations.
6614 * All direct and indirect children are processed in an unspecified order.
6615 * Note that if a child inherits from the original table via multiple
6616 * inheritance paths, it will be visited just once.
6619 ATSimpleRecursion(List
**wqueue
, Relation rel
,
6620 AlterTableCmd
*cmd
, bool recurse
, LOCKMODE lockmode
,
6621 AlterTableUtilityContext
*context
)
6624 * Propagate to children, if desired and if there are (or might be) any
6627 if (recurse
&& rel
->rd_rel
->relhassubclass
)
6629 Oid relid
= RelationGetRelid(rel
);
6633 children
= find_all_inheritors(relid
, lockmode
, NULL
);
6636 * find_all_inheritors does the recursive search of the inheritance
6637 * hierarchy, so all we have to do is process all of the relids in the
6638 * list that it returns.
6640 foreach(child
, children
)
6642 Oid childrelid
= lfirst_oid(child
);
6645 if (childrelid
== relid
)
6647 /* find_all_inheritors already got lock */
6648 childrel
= relation_open(childrelid
, NoLock
);
6649 CheckAlterTableIsSafe(childrel
);
6650 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
6651 relation_close(childrel
, NoLock
);
6657 * Obtain list of partitions of the given table, locking them all at the given
6658 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6660 * This function is a no-op if the given relation is not a partitioned table;
6661 * in particular, nothing is done if it's a legacy inheritance parent.
6664 ATCheckPartitionsNotInUse(Relation rel
, LOCKMODE lockmode
)
6666 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
6671 inh
= find_all_inheritors(RelationGetRelid(rel
), lockmode
, NULL
);
6672 /* first element is the parent rel; must ignore it */
6673 for_each_from(cell
, inh
, 1)
6677 /* find_all_inheritors already got lock */
6678 childrel
= table_open(lfirst_oid(cell
), NoLock
);
6679 CheckAlterTableIsSafe(childrel
);
6680 table_close(childrel
, NoLock
);
6687 * ATTypedTableRecursion
6689 * Propagate ALTER TYPE operations to the typed tables of that type.
6690 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6691 * recursion to inheritance children of the typed tables.
6694 ATTypedTableRecursion(List
**wqueue
, Relation rel
, AlterTableCmd
*cmd
,
6695 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
6700 Assert(rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6702 children
= find_typed_table_dependencies(rel
->rd_rel
->reltype
,
6703 RelationGetRelationName(rel
),
6706 foreach(child
, children
)
6708 Oid childrelid
= lfirst_oid(child
);
6711 childrel
= relation_open(childrelid
, lockmode
);
6712 CheckAlterTableIsSafe(childrel
);
6713 ATPrepCmd(wqueue
, childrel
, cmd
, true, true, lockmode
, context
);
6714 relation_close(childrel
, NoLock
);
6720 * find_composite_type_dependencies
6722 * Check to see if the type "typeOid" is being used as a column in some table
6723 * (possibly nested several levels deep in composite types, arrays, etc!).
6724 * Eventually, we'd like to propagate the check or rewrite operation
6725 * into such tables, but for now, just error out if we find any.
6727 * Caller should provide either the associated relation of a rowtype,
6728 * or a type name (not both) for use in the error message, if any.
6730 * Note that "typeOid" is not necessarily a composite type; it could also be
6731 * another container type such as an array or range, or a domain over one of
6732 * these things. The name of this function is therefore somewhat historical,
6733 * but it's not worth changing.
6735 * We assume that functions and views depending on the type are not reasons
6736 * to reject the ALTER. (How safe is this really?)
6739 find_composite_type_dependencies(Oid typeOid
, Relation origRelation
,
6740 const char *origTypeName
)
6744 SysScanDesc depScan
;
6747 /* since this function recurses, it could be driven to stack overflow */
6748 check_stack_depth();
6751 * We scan pg_depend to find those things that depend on the given type.
6752 * (We assume we can ignore refobjsubid for a type.)
6754 depRel
= table_open(DependRelationId
, AccessShareLock
);
6756 ScanKeyInit(&key
[0],
6757 Anum_pg_depend_refclassid
,
6758 BTEqualStrategyNumber
, F_OIDEQ
,
6759 ObjectIdGetDatum(TypeRelationId
));
6760 ScanKeyInit(&key
[1],
6761 Anum_pg_depend_refobjid
,
6762 BTEqualStrategyNumber
, F_OIDEQ
,
6763 ObjectIdGetDatum(typeOid
));
6765 depScan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
6768 while (HeapTupleIsValid(depTup
= systable_getnext(depScan
)))
6770 Form_pg_depend pg_depend
= (Form_pg_depend
) GETSTRUCT(depTup
);
6772 TupleDesc tupleDesc
;
6773 Form_pg_attribute att
;
6775 /* Check for directly dependent types */
6776 if (pg_depend
->classid
== TypeRelationId
)
6779 * This must be an array, domain, or range containing the given
6780 * type, so recursively check for uses of this type. Note that
6781 * any error message will mention the original type not the
6782 * container; this is intentional.
6784 find_composite_type_dependencies(pg_depend
->objid
,
6785 origRelation
, origTypeName
);
6789 /* Else, ignore dependees that aren't relations */
6790 if (pg_depend
->classid
!= RelationRelationId
)
6793 rel
= relation_open(pg_depend
->objid
, AccessShareLock
);
6794 tupleDesc
= RelationGetDescr(rel
);
6797 * If objsubid identifies a specific column, refer to that in error
6798 * messages. Otherwise, search to see if there's a user column of the
6799 * type. (We assume system columns are never of interesting types.)
6800 * The search is needed because an index containing an expression
6801 * column of the target type will just be recorded as a whole-relation
6802 * dependency. If we do not find a column of the type, the dependency
6803 * must indicate that the type is transiently referenced in an index
6804 * expression but not stored on disk, which we assume is OK, just as
6805 * we do for references in views. (It could also be that the target
6806 * type is embedded in some container type that is stored in an index
6807 * column, but the previous recursion should catch such cases.)
6809 if (pg_depend
->objsubid
> 0 && pg_depend
->objsubid
<= tupleDesc
->natts
)
6810 att
= TupleDescAttr(tupleDesc
, pg_depend
->objsubid
- 1);
6814 for (int attno
= 1; attno
<= tupleDesc
->natts
; attno
++)
6816 att
= TupleDescAttr(tupleDesc
, attno
- 1);
6817 if (att
->atttypid
== typeOid
&& !att
->attisdropped
)
6823 /* No such column, so assume OK */
6824 relation_close(rel
, AccessShareLock
);
6830 * We definitely should reject if the relation has storage. If it's
6831 * partitioned, then perhaps we don't have to reject: if there are
6832 * partitions then we'll fail when we find one, else there is no
6833 * stored data to worry about. However, it's possible that the type
6834 * change would affect conclusions about whether the type is sortable
6835 * or hashable and thus (if it's a partitioning column) break the
6836 * partitioning rule. For now, reject for partitioned rels too.
6838 if (RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
) ||
6839 RELKIND_HAS_PARTITIONS(rel
->rd_rel
->relkind
))
6843 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6844 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6846 RelationGetRelationName(rel
),
6847 NameStr(att
->attname
))));
6848 else if (origRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6850 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6851 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6852 RelationGetRelationName(origRelation
),
6853 RelationGetRelationName(rel
),
6854 NameStr(att
->attname
))));
6855 else if (origRelation
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
6857 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6858 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6859 RelationGetRelationName(origRelation
),
6860 RelationGetRelationName(rel
),
6861 NameStr(att
->attname
))));
6864 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
6865 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6866 RelationGetRelationName(origRelation
),
6867 RelationGetRelationName(rel
),
6868 NameStr(att
->attname
))));
6870 else if (OidIsValid(rel
->rd_rel
->reltype
))
6873 * A view or composite type itself isn't a problem, but we must
6874 * recursively check for indirect dependencies via its rowtype.
6876 find_composite_type_dependencies(rel
->rd_rel
->reltype
,
6877 origRelation
, origTypeName
);
6880 relation_close(rel
, AccessShareLock
);
6883 systable_endscan(depScan
);
6885 relation_close(depRel
, AccessShareLock
);
6890 * find_typed_table_dependencies
6892 * Check to see if a composite type is being used as the type of a
6893 * typed table. Abort if any are found and behavior is RESTRICT.
6894 * Else return the list of tables.
6897 find_typed_table_dependencies(Oid typeOid
, const char *typeName
, DropBehavior behavior
)
6905 classRel
= table_open(RelationRelationId
, AccessShareLock
);
6907 ScanKeyInit(&key
[0],
6908 Anum_pg_class_reloftype
,
6909 BTEqualStrategyNumber
, F_OIDEQ
,
6910 ObjectIdGetDatum(typeOid
));
6912 scan
= table_beginscan_catalog(classRel
, 1, key
);
6914 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
6916 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(tuple
);
6918 if (behavior
== DROP_RESTRICT
)
6920 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST
),
6921 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6923 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6925 result
= lappend_oid(result
, classform
->oid
);
6928 table_endscan(scan
);
6929 table_close(classRel
, AccessShareLock
);
6938 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6939 * isn't suitable, throw an error. Currently, we require that the type
6940 * originated with CREATE TYPE AS. We could support any row type, but doing so
6941 * would require handling a number of extra corner cases in the DDL commands.
6942 * (Also, allowing domain-over-composite would open up a can of worms about
6943 * whether and how the domain's constraints should apply to derived tables.)
6946 check_of_type(HeapTuple typetuple
)
6948 Form_pg_type typ
= (Form_pg_type
) GETSTRUCT(typetuple
);
6949 bool typeOk
= false;
6951 if (typ
->typtype
== TYPTYPE_COMPOSITE
)
6953 Relation typeRelation
;
6955 Assert(OidIsValid(typ
->typrelid
));
6956 typeRelation
= relation_open(typ
->typrelid
, AccessShareLock
);
6957 typeOk
= (typeRelation
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
);
6960 * Close the parent rel, but keep our AccessShareLock on it until xact
6961 * commit. That will prevent someone else from deleting or ALTERing
6962 * the type before the typed table creation/conversion commits.
6964 relation_close(typeRelation
, NoLock
);
6968 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6969 errmsg("type %s is not a composite type",
6970 format_type_be(typ
->oid
))));
6975 * ALTER TABLE ADD COLUMN
6977 * Adds an additional attribute to a relation making the assumption that
6978 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6979 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6982 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6983 * have to decide at runtime whether to recurse or not depending on whether we
6984 * actually add a column or merely merge with an existing column. (We can't
6985 * check this in a static pre-pass because it won't handle multiple inheritance
6986 * situations correctly.)
6989 ATPrepAddColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
6990 bool is_view
, AlterTableCmd
*cmd
, LOCKMODE lockmode
,
6991 AlterTableUtilityContext
*context
)
6993 if (rel
->rd_rel
->reloftype
&& !recursing
)
6995 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
6996 errmsg("cannot add column to typed table")));
6998 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
6999 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
7001 if (recurse
&& !is_view
)
7002 cmd
->recurse
= true;
7006 * Add a column to a table. The return value is the address of the
7007 * new column in the parent relation.
7009 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7010 * copy (but that happens only after we check for IF NOT EXISTS).
7012 static ObjectAddress
7013 ATExecAddColumn(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
7014 AlterTableCmd
**cmd
, bool recurse
, bool recursing
,
7015 LOCKMODE lockmode
, AlterTablePass cur_pass
,
7016 AlterTableUtilityContext
*context
)
7018 Oid myrelid
= RelationGetRelid(rel
);
7019 ColumnDef
*colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7020 bool if_not_exists
= (*cmd
)->missing_ok
;
7024 Form_pg_attribute attribute
;
7030 AlterTableCmd
*childcmd
;
7031 ObjectAddress address
;
7034 /* since this function recurses, it could be driven to stack overflow */
7035 check_stack_depth();
7037 /* At top level, permission check was done in ATPrepCmd, else do it */
7039 ATSimplePermissions((*cmd
)->subtype
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
7041 if (rel
->rd_rel
->relispartition
&& !recursing
)
7043 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
7044 errmsg("cannot add column to a partition")));
7046 attrdesc
= table_open(AttributeRelationId
, RowExclusiveLock
);
7049 * Are we adding the column to a recursion child? If so, check whether to
7050 * merge with an existing definition for the column. If we do merge, we
7051 * must not recurse. Children will already have the column, and recursing
7052 * into them would mess up attinhcount.
7054 if (colDef
->inhcount
> 0)
7058 /* Does child already have a column by this name? */
7059 tuple
= SearchSysCacheCopyAttName(myrelid
, colDef
->colname
);
7060 if (HeapTupleIsValid(tuple
))
7062 Form_pg_attribute childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7067 /* Child column must match on type, typmod, and collation */
7068 typenameTypeIdAndMod(NULL
, colDef
->typeName
, &ctypeId
, &ctypmod
);
7069 if (ctypeId
!= childatt
->atttypid
||
7070 ctypmod
!= childatt
->atttypmod
)
7072 (errcode(ERRCODE_DATATYPE_MISMATCH
),
7073 errmsg("child table \"%s\" has different type for column \"%s\"",
7074 RelationGetRelationName(rel
), colDef
->colname
)));
7075 ccollid
= GetColumnDefCollation(NULL
, colDef
, ctypeId
);
7076 if (ccollid
!= childatt
->attcollation
)
7078 (errcode(ERRCODE_COLLATION_MISMATCH
),
7079 errmsg("child table \"%s\" has different collation for column \"%s\"",
7080 RelationGetRelationName(rel
), colDef
->colname
),
7081 errdetail("\"%s\" versus \"%s\"",
7082 get_collation_name(ccollid
),
7083 get_collation_name(childatt
->attcollation
))));
7085 /* Bump the existing child att's inhcount */
7086 childatt
->attinhcount
++;
7087 if (childatt
->attinhcount
< 0)
7089 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
7090 errmsg("too many inheritance parents"));
7091 CatalogTupleUpdate(attrdesc
, &tuple
->t_self
, tuple
);
7093 heap_freetuple(tuple
);
7095 /* Inform the user about the merge */
7097 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7098 colDef
->colname
, RelationGetRelationName(rel
))));
7100 table_close(attrdesc
, RowExclusiveLock
);
7102 /* Make the child column change visible */
7103 CommandCounterIncrement();
7105 return InvalidObjectAddress
;
7109 /* skip if the name already exists and if_not_exists is true */
7110 if (!check_for_column_name_collision(rel
, colDef
->colname
, if_not_exists
))
7112 table_close(attrdesc
, RowExclusiveLock
);
7113 return InvalidObjectAddress
;
7117 * Okay, we need to add the column, so go ahead and do parse
7118 * transformation. This can result in queueing up, or even immediately
7119 * executing, subsidiary operations (such as creation of unique indexes);
7120 * so we mustn't do it until we have made the if_not_exists check.
7122 * When recursing, the command was already transformed and we needn't do
7123 * so again. Also, if context isn't given we can't transform. (That
7124 * currently happens only for AT_AddColumnToView; we expect that view.c
7125 * passed us a ColumnDef that doesn't need work.)
7127 if (context
!= NULL
&& !recursing
)
7129 *cmd
= ATParseTransformCmd(wqueue
, tab
, rel
, *cmd
, recurse
, lockmode
,
7131 Assert(*cmd
!= NULL
);
7132 colDef
= castNode(ColumnDef
, (*cmd
)->def
);
7136 * Regular inheritance children are independent enough not to inherit the
7137 * identity column from parent hence cannot recursively add identity
7138 * column if the table has inheritance children.
7140 * Partitions, on the other hand, are integral part of a partitioned table
7141 * and inherit identity column. Hence propagate identity column down the
7142 * partition hierarchy.
7144 if (colDef
->identity
&&
7146 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
&&
7147 find_inheritance_children(myrelid
, NoLock
) != NIL
)
7149 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7150 errmsg("cannot recursively add identity column to table that has child tables")));
7152 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
7154 reltup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(myrelid
));
7155 if (!HeapTupleIsValid(reltup
))
7156 elog(ERROR
, "cache lookup failed for relation %u", myrelid
);
7157 relkind
= ((Form_pg_class
) GETSTRUCT(reltup
))->relkind
;
7159 /* Determine the new attribute's number */
7160 newattnum
= ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
+ 1;
7161 if (newattnum
> MaxHeapAttributeNumber
)
7163 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
7164 errmsg("tables can have at most %d columns",
7165 MaxHeapAttributeNumber
)));
7168 * Construct new attribute's pg_attribute entry.
7170 tupdesc
= BuildDescForRelation(list_make1(colDef
));
7172 attribute
= TupleDescAttr(tupdesc
, 0);
7174 /* Fix up attribute number */
7175 attribute
->attnum
= newattnum
;
7177 /* make sure datatype is legal for a column */
7178 CheckAttributeType(NameStr(attribute
->attname
), attribute
->atttypid
, attribute
->attcollation
,
7179 list_make1_oid(rel
->rd_rel
->reltype
),
7182 InsertPgAttributeTuples(attrdesc
, tupdesc
, myrelid
, NULL
, NULL
);
7184 table_close(attrdesc
, RowExclusiveLock
);
7187 * Update pg_class tuple as appropriate
7189 ((Form_pg_class
) GETSTRUCT(reltup
))->relnatts
= newattnum
;
7191 CatalogTupleUpdate(pgclass
, &reltup
->t_self
, reltup
);
7193 heap_freetuple(reltup
);
7195 /* Post creation hook for new attribute */
7196 InvokeObjectPostCreateHook(RelationRelationId
, myrelid
, newattnum
);
7198 table_close(pgclass
, RowExclusiveLock
);
7200 /* Make the attribute's catalog entry visible */
7201 CommandCounterIncrement();
7204 * Store the DEFAULT, if any, in the catalogs
7206 if (colDef
->raw_default
)
7208 RawColumnDefault
*rawEnt
;
7210 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7211 rawEnt
->attnum
= attribute
->attnum
;
7212 rawEnt
->raw_default
= copyObject(colDef
->raw_default
);
7215 * Attempt to skip a complete table rewrite by storing the specified
7216 * DEFAULT value outside of the heap. This may be disabled inside
7217 * AddRelationNewConstraints if the optimization cannot be applied.
7219 rawEnt
->missingMode
= (!colDef
->generated
);
7221 rawEnt
->generated
= colDef
->generated
;
7224 * This function is intended for CREATE TABLE, so it processes a
7225 * _list_ of defaults, but we just do one.
7227 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7228 false, true, false, NULL
);
7230 /* Make the additional catalog changes visible */
7231 CommandCounterIncrement();
7234 * Did the request for a missing value work? If not we'll have to do a
7237 if (!rawEnt
->missingMode
)
7238 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7242 * Tell Phase 3 to fill in the default expression, if there is one.
7244 * If there is no default, Phase 3 doesn't have to do anything, because
7245 * that effectively means that the default is NULL. The heap tuple access
7246 * routines always check for attnum > # of attributes in tuple, and return
7247 * NULL if so, so without any modification of the tuple data we will get
7248 * the effect of NULL values in the new column.
7250 * An exception occurs when the new column is of a domain type: the domain
7251 * might have a not-null constraint, or a check constraint that indirectly
7252 * rejects nulls. If there are any domain constraints then we construct
7253 * an explicit NULL default value that will be passed through
7254 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7255 * rewriting the table which we really don't have to do, but the present
7256 * design of domain processing doesn't offer any simple way of checking
7257 * the constraints more directly.)
7259 * Note: we use build_column_default, and not just the cooked default
7260 * returned by AddRelationNewConstraints, so that the right thing happens
7261 * when a datatype's default applies.
7263 * Note: it might seem that this should happen at the end of Phase 2, so
7264 * that the effects of subsequent subcommands can be taken into account.
7265 * It's intentional that we do it now, though. The new column should be
7266 * filled according to what is said in the ADD COLUMN subcommand, so that
7267 * the effects are the same as if this subcommand had been run by itself
7268 * and the later subcommands had been issued in new ALTER TABLE commands.
7270 * We can skip this entirely for relations without storage, since Phase 3
7271 * is certainly not going to touch them. System attributes don't have
7272 * interesting defaults, either.
7274 if (RELKIND_HAS_STORAGE(relkind
))
7277 * For an identity column, we can't use build_column_default(),
7278 * because the sequence ownership isn't set yet. So do it manually.
7280 if (colDef
->identity
)
7282 NextValueExpr
*nve
= makeNode(NextValueExpr
);
7284 nve
->seqid
= RangeVarGetRelid(colDef
->identitySequence
, NoLock
, false);
7285 nve
->typeId
= attribute
->atttypid
;
7287 defval
= (Expr
*) nve
;
7289 /* must do a rewrite for identity columns */
7290 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7293 defval
= (Expr
*) build_column_default(rel
, attribute
->attnum
);
7295 if (!defval
&& DomainHasConstraints(attribute
->atttypid
))
7301 baseTypeMod
= attribute
->atttypmod
;
7302 baseTypeId
= getBaseTypeAndTypmod(attribute
->atttypid
, &baseTypeMod
);
7303 baseTypeColl
= get_typcollation(baseTypeId
);
7304 defval
= (Expr
*) makeNullConst(baseTypeId
, baseTypeMod
, baseTypeColl
);
7305 defval
= (Expr
*) coerce_to_target_type(NULL
,
7308 attribute
->atttypid
,
7309 attribute
->atttypmod
,
7310 COERCION_ASSIGNMENT
,
7311 COERCE_IMPLICIT_CAST
,
7313 if (defval
== NULL
) /* should not happen */
7314 elog(ERROR
, "failed to coerce base type to domain");
7319 NewColumnValue
*newval
;
7321 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
7322 newval
->attnum
= attribute
->attnum
;
7323 newval
->expr
= expression_planner(defval
);
7324 newval
->is_generated
= (colDef
->generated
!= '\0');
7326 tab
->newvals
= lappend(tab
->newvals
, newval
);
7329 if (DomainHasConstraints(attribute
->atttypid
))
7330 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
7332 if (!TupleDescAttr(rel
->rd_att
, attribute
->attnum
- 1)->atthasmissing
)
7335 * If the new column is NOT NULL, and there is no missing value,
7336 * tell Phase 3 it needs to check for NULLs.
7338 tab
->verify_new_notnull
|= colDef
->is_not_null
;
7343 * Add needed dependency entries for the new column.
7345 add_column_datatype_dependency(myrelid
, newattnum
, attribute
->atttypid
);
7346 add_column_collation_dependency(myrelid
, newattnum
, attribute
->attcollation
);
7349 * Propagate to children as appropriate. Unlike most other ALTER
7350 * routines, we have to do this one level of recursion at a time; we can't
7351 * use find_all_inheritors to do it in one pass.
7354 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
7357 * If we are told not to recurse, there had better not be any child
7358 * tables; else the addition would put them out of step.
7360 if (children
&& !recurse
)
7362 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7363 errmsg("column must be added to child tables too")));
7365 /* Children should see column as singly inherited */
7368 childcmd
= copyObject(*cmd
);
7369 colDef
= castNode(ColumnDef
, childcmd
->def
);
7370 colDef
->inhcount
= 1;
7371 colDef
->is_local
= false;
7374 childcmd
= *cmd
; /* no need to copy again */
7376 foreach(child
, children
)
7378 Oid childrelid
= lfirst_oid(child
);
7380 AlteredTableInfo
*childtab
;
7382 /* find_inheritance_children already got lock */
7383 childrel
= table_open(childrelid
, NoLock
);
7384 CheckAlterTableIsSafe(childrel
);
7386 /* Find or create work queue entry for this table */
7387 childtab
= ATGetQueueEntry(wqueue
, childrel
);
7389 /* Recurse to child; return value is ignored */
7390 ATExecAddColumn(wqueue
, childtab
, childrel
,
7391 &childcmd
, recurse
, true,
7392 lockmode
, cur_pass
, context
);
7394 table_close(childrel
, NoLock
);
7397 ObjectAddressSubSet(address
, RelationRelationId
, myrelid
, newattnum
);
7402 * If a new or renamed column will collide with the name of an existing
7403 * column and if_not_exists is false then error out, else do nothing.
7406 check_for_column_name_collision(Relation rel
, const char *colname
,
7413 * this test is deliberately not attisdropped-aware, since if one tries to
7414 * add a column matching a dropped column name, it's gonna fail anyway.
7416 attTuple
= SearchSysCache2(ATTNAME
,
7417 ObjectIdGetDatum(RelationGetRelid(rel
)),
7418 PointerGetDatum(colname
));
7419 if (!HeapTupleIsValid(attTuple
))
7422 attnum
= ((Form_pg_attribute
) GETSTRUCT(attTuple
))->attnum
;
7423 ReleaseSysCache(attTuple
);
7426 * We throw a different error message for conflicts with system column
7427 * names, since they are normally not shown and the user might otherwise
7428 * be confused about the reason for the conflict.
7432 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7433 errmsg("column name \"%s\" conflicts with a system column name",
7440 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7441 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7442 colname
, RelationGetRelationName(rel
))));
7447 (errcode(ERRCODE_DUPLICATE_COLUMN
),
7448 errmsg("column \"%s\" of relation \"%s\" already exists",
7449 colname
, RelationGetRelationName(rel
))));
7456 * Install a column's dependency on its datatype.
7459 add_column_datatype_dependency(Oid relid
, int32 attnum
, Oid typid
)
7461 ObjectAddress myself
,
7464 myself
.classId
= RelationRelationId
;
7465 myself
.objectId
= relid
;
7466 myself
.objectSubId
= attnum
;
7467 referenced
.classId
= TypeRelationId
;
7468 referenced
.objectId
= typid
;
7469 referenced
.objectSubId
= 0;
7470 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7474 * Install a column's dependency on its collation.
7477 add_column_collation_dependency(Oid relid
, int32 attnum
, Oid collid
)
7479 ObjectAddress myself
,
7482 /* We know the default collation is pinned, so don't bother recording it */
7483 if (OidIsValid(collid
) && collid
!= DEFAULT_COLLATION_OID
)
7485 myself
.classId
= RelationRelationId
;
7486 myself
.objectId
= relid
;
7487 myself
.objectSubId
= attnum
;
7488 referenced
.classId
= CollationRelationId
;
7489 referenced
.objectId
= collid
;
7490 referenced
.objectSubId
= 0;
7491 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
7496 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7500 ATPrepDropNotNull(Relation rel
, bool recurse
, bool recursing
)
7503 * If the parent is a partitioned table, like check constraints, we do not
7504 * support removing the NOT NULL while partitions exist.
7506 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7508 PartitionDesc partdesc
= RelationGetPartitionDesc(rel
, true);
7510 Assert(partdesc
!= NULL
);
7511 if (partdesc
->nparts
> 0 && !recurse
&& !recursing
)
7513 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7514 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7515 errhint("Do not specify the ONLY keyword.")));
7520 * Return the address of the modified column. If the column was already
7521 * nullable, InvalidObjectAddress is returned.
7523 static ObjectAddress
7524 ATExecDropNotNull(Relation rel
, const char *colName
, LOCKMODE lockmode
)
7527 Form_pg_attribute attTup
;
7531 ObjectAddress address
;
7534 * lookup the attribute
7536 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7538 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7539 if (!HeapTupleIsValid(tuple
))
7541 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7542 errmsg("column \"%s\" of relation \"%s\" does not exist",
7543 colName
, RelationGetRelationName(rel
))));
7544 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
7545 attnum
= attTup
->attnum
;
7547 /* Prevent them from altering a system attribute */
7550 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7551 errmsg("cannot alter system column \"%s\"",
7554 if (attTup
->attidentity
)
7556 (errcode(ERRCODE_SYNTAX_ERROR
),
7557 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7558 colName
, RelationGetRelationName(rel
))));
7561 * Check that the attribute is not in a primary key or in an index used as
7562 * a replica identity.
7564 * Note: we'll throw error even if the pkey index is not valid.
7567 /* Loop over all indexes on the relation */
7568 indexoidlist
= RelationGetIndexList(rel
);
7570 foreach_oid(indexoid
, indexoidlist
)
7572 HeapTuple indexTuple
;
7573 Form_pg_index indexStruct
;
7575 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
7576 if (!HeapTupleIsValid(indexTuple
))
7577 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
7578 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
7581 * If the index is not a primary key or an index used as replica
7582 * identity, skip the check.
7584 if (indexStruct
->indisprimary
|| indexStruct
->indisreplident
)
7587 * Loop over each attribute in the primary key or the index used
7588 * as replica identity and see if it matches the to-be-altered
7591 for (int i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
7593 if (indexStruct
->indkey
.values
[i
] == attnum
)
7595 if (indexStruct
->indisprimary
)
7597 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7598 errmsg("column \"%s\" is in a primary key",
7602 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7603 errmsg("column \"%s\" is in index used as replica identity",
7609 ReleaseSysCache(indexTuple
);
7612 list_free(indexoidlist
);
7614 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7615 if (rel
->rd_rel
->relispartition
)
7617 Oid parentId
= get_partition_parent(RelationGetRelid(rel
), false);
7618 Relation parent
= table_open(parentId
, AccessShareLock
);
7619 TupleDesc tupDesc
= RelationGetDescr(parent
);
7620 AttrNumber parent_attnum
;
7622 parent_attnum
= get_attnum(parentId
, colName
);
7623 if (TupleDescAttr(tupDesc
, parent_attnum
- 1)->attnotnull
)
7625 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7626 errmsg("column \"%s\" is marked NOT NULL in parent table",
7628 table_close(parent
, AccessShareLock
);
7632 * Okay, actually perform the catalog change ... if needed
7634 if (attTup
->attnotnull
)
7636 attTup
->attnotnull
= false;
7638 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7640 ObjectAddressSubSet(address
, RelationRelationId
,
7641 RelationGetRelid(rel
), attnum
);
7644 address
= InvalidObjectAddress
;
7646 InvokeObjectPostAlterHook(RelationRelationId
,
7647 RelationGetRelid(rel
), attnum
);
7649 table_close(attr_rel
, RowExclusiveLock
);
7655 * ALTER TABLE ALTER COLUMN SET NOT NULL
7659 ATPrepSetNotNull(List
**wqueue
, Relation rel
,
7660 AlterTableCmd
*cmd
, bool recurse
, bool recursing
,
7661 LOCKMODE lockmode
, AlterTableUtilityContext
*context
)
7664 * If we're already recursing, there's nothing to do; the topmost
7665 * invocation of ATSimpleRecursion already visited all children.
7671 * If the target column is already marked NOT NULL, we can skip recursing
7672 * to children, because their columns should already be marked NOT NULL as
7673 * well. But there's no point in checking here unless the relation has
7674 * some children; else we can just wait till execution to check. (If it
7675 * does have children, however, this can save taking per-child locks
7676 * unnecessarily. This greatly improves concurrency in some parallel
7677 * restore scenarios.)
7679 * Unfortunately, we can only apply this optimization to partitioned
7680 * tables, because traditional inheritance doesn't enforce that child
7681 * columns be NOT NULL when their parent is. (That's a bug that should
7682 * get fixed someday.)
7684 if (rel
->rd_rel
->relhassubclass
&&
7685 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
7690 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), cmd
->name
);
7692 /* Might as well throw the error now, if name is bad */
7693 if (!HeapTupleIsValid(tuple
))
7695 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7696 errmsg("column \"%s\" of relation \"%s\" does not exist",
7697 cmd
->name
, RelationGetRelationName(rel
))));
7699 attnotnull
= ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
;
7700 ReleaseSysCache(tuple
);
7706 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7707 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7708 * normal recursion logic.
7710 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
7713 AlterTableCmd
*newcmd
= makeNode(AlterTableCmd
);
7715 newcmd
->subtype
= AT_CheckNotNull
;
7716 newcmd
->name
= pstrdup(cmd
->name
);
7717 ATSimpleRecursion(wqueue
, rel
, newcmd
, true, lockmode
, context
);
7720 ATSimpleRecursion(wqueue
, rel
, cmd
, recurse
, lockmode
, context
);
7724 * Return the address of the modified column. If the column was already NOT
7725 * NULL, InvalidObjectAddress is returned.
7727 static ObjectAddress
7728 ATExecSetNotNull(AlteredTableInfo
*tab
, Relation rel
,
7729 const char *colName
, LOCKMODE lockmode
)
7734 ObjectAddress address
;
7737 * lookup the attribute
7739 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
7741 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
7743 if (!HeapTupleIsValid(tuple
))
7745 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7746 errmsg("column \"%s\" of relation \"%s\" does not exist",
7747 colName
, RelationGetRelationName(rel
))));
7749 attnum
= ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnum
;
7751 /* Prevent them from altering a system attribute */
7754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7755 errmsg("cannot alter system column \"%s\"",
7759 * Okay, actually perform the catalog change ... if needed
7761 if (!((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
)
7763 ((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
= true;
7765 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
7768 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7769 * are set NOT NULL; however, if we can find a constraint which proves
7770 * this then we can skip that. We needn't bother looking if we've
7771 * already found that we must verify some other not-null constraint.
7773 if (!tab
->verify_new_notnull
&&
7774 !NotNullImpliedByRelConstraints(rel
, (Form_pg_attribute
) GETSTRUCT(tuple
)))
7776 /* Tell Phase 3 it needs to test the constraint */
7777 tab
->verify_new_notnull
= true;
7780 ObjectAddressSubSet(address
, RelationRelationId
,
7781 RelationGetRelid(rel
), attnum
);
7784 address
= InvalidObjectAddress
;
7786 InvokeObjectPostAlterHook(RelationRelationId
,
7787 RelationGetRelid(rel
), attnum
);
7789 table_close(attr_rel
, RowExclusiveLock
);
7795 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7797 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7798 * commands against the partitions of a partitioned table if the user
7799 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7800 * or tries to create a primary key on it (which internally creates
7801 * AT_SetNotNull on the partitioned table). Such a command doesn't
7802 * allow us to actually modify any partition, but we want to let it
7803 * go through if the partitions are already properly marked.
7805 * In future, this might need to adjust the child table's state, likely
7806 * by incrementing an inheritance count for the attnotnull constraint.
7807 * For now we need only check for the presence of the flag.
7810 ATExecCheckNotNull(AlteredTableInfo
*tab
, Relation rel
,
7811 const char *colName
, LOCKMODE lockmode
)
7815 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
7817 if (!HeapTupleIsValid(tuple
))
7819 errcode(ERRCODE_UNDEFINED_COLUMN
),
7820 errmsg("column \"%s\" of relation \"%s\" does not exist",
7821 colName
, RelationGetRelationName(rel
)));
7823 if (!((Form_pg_attribute
) GETSTRUCT(tuple
))->attnotnull
)
7825 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
7826 errmsg("constraint must be added to child tables too"),
7827 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7828 colName
, RelationGetRelationName(rel
)),
7829 errhint("Do not specify the ONLY keyword.")));
7831 ReleaseSysCache(tuple
);
7835 * NotNullImpliedByRelConstraints
7836 * Does rel's existing constraints imply NOT NULL for the given attribute?
7839 NotNullImpliedByRelConstraints(Relation rel
, Form_pg_attribute attr
)
7841 NullTest
*nnulltest
= makeNode(NullTest
);
7843 nnulltest
->arg
= (Expr
*) makeVar(1,
7849 nnulltest
->nulltesttype
= IS_NOT_NULL
;
7852 * argisrow = false is correct even for a composite column, because
7853 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7854 * case, just IS DISTINCT FROM NULL.
7856 nnulltest
->argisrow
= false;
7857 nnulltest
->location
= -1;
7859 if (ConstraintImpliedByRelConstraint(rel
, list_make1(nnulltest
), NIL
))
7862 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7863 RelationGetRelationName(rel
), NameStr(attr
->attname
))));
7871 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7873 * Return the address of the affected column.
7875 static ObjectAddress
7876 ATExecColumnDefault(Relation rel
, const char *colName
,
7877 Node
*newDefault
, LOCKMODE lockmode
)
7879 TupleDesc tupdesc
= RelationGetDescr(rel
);
7881 ObjectAddress address
;
7884 * get the number of the attribute
7886 attnum
= get_attnum(RelationGetRelid(rel
), colName
);
7887 if (attnum
== InvalidAttrNumber
)
7889 (errcode(ERRCODE_UNDEFINED_COLUMN
),
7890 errmsg("column \"%s\" of relation \"%s\" does not exist",
7891 colName
, RelationGetRelationName(rel
))));
7893 /* Prevent them from altering a system attribute */
7896 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
7897 errmsg("cannot alter system column \"%s\"",
7900 if (TupleDescAttr(tupdesc
, attnum
- 1)->attidentity
)
7902 (errcode(ERRCODE_SYNTAX_ERROR
),
7903 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7904 colName
, RelationGetRelationName(rel
)),
7905 /* translator: %s is an SQL ALTER command */
7906 newDefault
? 0 : errhint("Use %s instead.",
7907 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7909 if (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
)
7911 (errcode(ERRCODE_SYNTAX_ERROR
),
7912 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7913 colName
, RelationGetRelationName(rel
)),
7915 /* translator: %s is an SQL ALTER command */
7916 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7917 (TupleDescAttr(tupdesc
, attnum
- 1)->attgenerated
== ATTRIBUTE_GENERATED_STORED
?
7918 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7921 * Remove any old default for the column. We use RESTRICT here for
7922 * safety, but at present we do not expect anything to depend on the
7925 * We treat removing the existing default as an internal operation when it
7926 * is preparatory to adding a new default, but as a user-initiated
7927 * operation when the user asked for a drop.
7929 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
7930 newDefault
!= NULL
);
7935 RawColumnDefault
*rawEnt
;
7937 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
7938 rawEnt
->attnum
= attnum
;
7939 rawEnt
->raw_default
= newDefault
;
7940 rawEnt
->missingMode
= false;
7941 rawEnt
->generated
= '\0';
7944 * This function is intended for CREATE TABLE, so it processes a
7945 * _list_ of defaults, but we just do one.
7947 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
7948 false, true, false, NULL
);
7951 ObjectAddressSubSet(address
, RelationRelationId
,
7952 RelationGetRelid(rel
), attnum
);
7957 * Add a pre-cooked default expression.
7959 * Return the address of the affected column.
7961 static ObjectAddress
7962 ATExecCookedColumnDefault(Relation rel
, AttrNumber attnum
,
7965 ObjectAddress address
;
7967 /* We assume no checking is required */
7970 * Remove any old default for the column. We use RESTRICT here for
7971 * safety, but at present we do not expect anything to depend on the
7972 * default. (In ordinary cases, there could not be a default in place
7973 * anyway, but it's possible when combining LIKE with inheritance.)
7975 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, false,
7978 (void) StoreAttrDefault(rel
, attnum
, newDefault
, true, false);
7980 ObjectAddressSubSet(address
, RelationRelationId
,
7981 RelationGetRelid(rel
), attnum
);
7986 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7988 * Return the address of the affected column.
7990 static ObjectAddress
7991 ATExecAddIdentity(Relation rel
, const char *colName
,
7992 Node
*def
, LOCKMODE lockmode
, bool recurse
, bool recursing
)
7994 Relation attrelation
;
7996 Form_pg_attribute attTup
;
7998 ObjectAddress address
;
7999 ColumnDef
*cdef
= castNode(ColumnDef
, def
);
8002 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8003 if (ispartitioned
&& !recurse
)
8005 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8006 errmsg("cannot add identity to a column of only the partitioned table"),
8007 errhint("Do not specify the ONLY keyword.")));
8009 if (rel
->rd_rel
->relispartition
&& !recursing
)
8011 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8012 errmsg("cannot add identity to a column of a partition"));
8014 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8016 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8017 if (!HeapTupleIsValid(tuple
))
8019 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8020 errmsg("column \"%s\" of relation \"%s\" does not exist",
8021 colName
, RelationGetRelationName(rel
))));
8022 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8023 attnum
= attTup
->attnum
;
8025 /* Can't alter a system attribute */
8028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8029 errmsg("cannot alter system column \"%s\"",
8033 * Creating a column as identity implies NOT NULL, so adding the identity
8034 * to an existing column that is not NOT NULL would create a state that
8035 * cannot be reproduced without contortions.
8037 if (!attTup
->attnotnull
)
8039 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8040 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8041 colName
, RelationGetRelationName(rel
))));
8043 if (attTup
->attidentity
)
8045 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8046 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8047 colName
, RelationGetRelationName(rel
))));
8049 if (attTup
->atthasdef
)
8051 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8052 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8053 colName
, RelationGetRelationName(rel
))));
8055 attTup
->attidentity
= cdef
->identity
;
8056 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8058 InvokeObjectPostAlterHook(RelationRelationId
,
8059 RelationGetRelid(rel
),
8061 ObjectAddressSubSet(address
, RelationRelationId
,
8062 RelationGetRelid(rel
), attnum
);
8063 heap_freetuple(tuple
);
8065 table_close(attrelation
, RowExclusiveLock
);
8068 * Recurse to propagate the identity column to partitions. Identity is
8069 * not inherited in regular inheritance children.
8071 if (recurse
&& ispartitioned
)
8076 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8078 foreach(lc
, children
)
8082 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8083 ATExecAddIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8084 table_close(childrel
, NoLock
);
8092 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8094 * Return the address of the affected column.
8096 static ObjectAddress
8097 ATExecSetIdentity(Relation rel
, const char *colName
, Node
*def
,
8098 LOCKMODE lockmode
, bool recurse
, bool recursing
)
8101 DefElem
*generatedEl
= NULL
;
8103 Form_pg_attribute attTup
;
8105 Relation attrelation
;
8106 ObjectAddress address
;
8109 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8110 if (ispartitioned
&& !recurse
)
8112 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8113 errmsg("cannot change identity column of only the partitioned table"),
8114 errhint("Do not specify the ONLY keyword.")));
8116 if (rel
->rd_rel
->relispartition
&& !recursing
)
8118 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8119 errmsg("cannot change identity column of a partition"));
8121 foreach(option
, castNode(List
, def
))
8123 DefElem
*defel
= lfirst_node(DefElem
, option
);
8125 if (strcmp(defel
->defname
, "generated") == 0)
8129 (errcode(ERRCODE_SYNTAX_ERROR
),
8130 errmsg("conflicting or redundant options")));
8131 generatedEl
= defel
;
8134 elog(ERROR
, "option \"%s\" not recognized",
8139 * Even if there is nothing to change here, we run all the checks. There
8140 * will be a subsequent ALTER SEQUENCE that relies on everything being
8144 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8145 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8146 if (!HeapTupleIsValid(tuple
))
8148 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8149 errmsg("column \"%s\" of relation \"%s\" does not exist",
8150 colName
, RelationGetRelationName(rel
))));
8152 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8153 attnum
= attTup
->attnum
;
8157 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8158 errmsg("cannot alter system column \"%s\"",
8161 if (!attTup
->attidentity
)
8163 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8164 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8165 colName
, RelationGetRelationName(rel
))));
8169 attTup
->attidentity
= defGetInt32(generatedEl
);
8170 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8172 InvokeObjectPostAlterHook(RelationRelationId
,
8173 RelationGetRelid(rel
),
8175 ObjectAddressSubSet(address
, RelationRelationId
,
8176 RelationGetRelid(rel
), attnum
);
8179 address
= InvalidObjectAddress
;
8181 heap_freetuple(tuple
);
8182 table_close(attrelation
, RowExclusiveLock
);
8185 * Recurse to propagate the identity change to partitions. Identity is not
8186 * inherited in regular inheritance children.
8188 if (generatedEl
&& recurse
&& ispartitioned
)
8193 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8195 foreach(lc
, children
)
8199 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8200 ATExecSetIdentity(childrel
, colName
, def
, lockmode
, recurse
, true);
8201 table_close(childrel
, NoLock
);
8209 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8211 * Return the address of the affected column.
8213 static ObjectAddress
8214 ATExecDropIdentity(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
,
8215 bool recurse
, bool recursing
)
8218 Form_pg_attribute attTup
;
8220 Relation attrelation
;
8221 ObjectAddress address
;
8223 ObjectAddress seqaddress
;
8226 ispartitioned
= (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
8227 if (ispartitioned
&& !recurse
)
8229 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8230 errmsg("cannot drop identity from a column of only the partitioned table"),
8231 errhint("Do not specify the ONLY keyword.")));
8233 if (rel
->rd_rel
->relispartition
&& !recursing
)
8235 errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8236 errmsg("cannot drop identity from a column of a partition"));
8238 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8239 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8240 if (!HeapTupleIsValid(tuple
))
8242 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8243 errmsg("column \"%s\" of relation \"%s\" does not exist",
8244 colName
, RelationGetRelationName(rel
))));
8246 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8247 attnum
= attTup
->attnum
;
8251 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8252 errmsg("cannot alter system column \"%s\"",
8255 if (!attTup
->attidentity
)
8259 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8260 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8261 colName
, RelationGetRelationName(rel
))));
8265 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8266 colName
, RelationGetRelationName(rel
))));
8267 heap_freetuple(tuple
);
8268 table_close(attrelation
, RowExclusiveLock
);
8269 return InvalidObjectAddress
;
8273 attTup
->attidentity
= '\0';
8274 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8276 InvokeObjectPostAlterHook(RelationRelationId
,
8277 RelationGetRelid(rel
),
8279 ObjectAddressSubSet(address
, RelationRelationId
,
8280 RelationGetRelid(rel
), attnum
);
8281 heap_freetuple(tuple
);
8283 table_close(attrelation
, RowExclusiveLock
);
8286 * Recurse to drop the identity from column in partitions. Identity is
8287 * not inherited in regular inheritance children so ignore them.
8289 if (recurse
&& ispartitioned
)
8294 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
8296 foreach(lc
, children
)
8300 childrel
= table_open(lfirst_oid(lc
), NoLock
);
8301 ATExecDropIdentity(childrel
, colName
, false, lockmode
, recurse
, true);
8302 table_close(childrel
, NoLock
);
8308 /* drop the internal sequence */
8309 seqid
= getIdentitySequence(rel
, attnum
, false);
8310 deleteDependencyRecordsForClass(RelationRelationId
, seqid
,
8311 RelationRelationId
, DEPENDENCY_INTERNAL
);
8312 CommandCounterIncrement();
8313 seqaddress
.classId
= RelationRelationId
;
8314 seqaddress
.objectId
= seqid
;
8315 seqaddress
.objectSubId
= 0;
8316 performDeletion(&seqaddress
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
8323 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8325 * Return the address of the affected column.
8327 static ObjectAddress
8328 ATExecSetExpression(AlteredTableInfo
*tab
, Relation rel
, const char *colName
,
8329 Node
*newExpr
, LOCKMODE lockmode
)
8332 Form_pg_attribute attTup
;
8335 ObjectAddress address
;
8337 NewColumnValue
*newval
;
8338 RawColumnDefault
*rawEnt
;
8340 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8341 if (!HeapTupleIsValid(tuple
))
8343 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8344 errmsg("column \"%s\" of relation \"%s\" does not exist",
8345 colName
, RelationGetRelationName(rel
))));
8347 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8348 attnum
= attTup
->attnum
;
8352 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8353 errmsg("cannot alter system column \"%s\"",
8356 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8358 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8359 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8360 colName
, RelationGetRelationName(rel
))));
8361 ReleaseSysCache(tuple
);
8364 * Clear all the missing values if we're rewriting the table, since this
8365 * renders them pointless.
8367 RelationClearMissing(rel
);
8369 /* make sure we don't conflict with later attribute modifications */
8370 CommandCounterIncrement();
8373 * Find everything that depends on the column (constraints, indexes, etc),
8374 * and record enough information to let us recreate the objects after
8377 RememberAllDependentForRebuilding(tab
, AT_SetExpression
, rel
, attnum
, colName
);
8380 * Drop the dependency records of the GENERATED expression, in particular
8381 * its INTERNAL dependency on the column, which would otherwise cause
8382 * dependency.c to refuse to perform the deletion.
8384 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8385 if (!OidIsValid(attrdefoid
))
8386 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8387 RelationGetRelid(rel
), attnum
);
8388 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8390 /* Make above changes visible */
8391 CommandCounterIncrement();
8394 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8395 * safety, but at present we do not expect anything to depend on the
8398 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8401 /* Prepare to store the new expression, in the catalogs */
8402 rawEnt
= (RawColumnDefault
*) palloc(sizeof(RawColumnDefault
));
8403 rawEnt
->attnum
= attnum
;
8404 rawEnt
->raw_default
= newExpr
;
8405 rawEnt
->missingMode
= false;
8406 rawEnt
->generated
= ATTRIBUTE_GENERATED_STORED
;
8408 /* Store the generated expression */
8409 AddRelationNewConstraints(rel
, list_make1(rawEnt
), NIL
,
8410 false, true, false, NULL
);
8412 /* Make above new expression visible */
8413 CommandCounterIncrement();
8415 /* Prepare for table rewrite */
8416 defval
= (Expr
*) build_column_default(rel
, attnum
);
8418 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
8419 newval
->attnum
= attnum
;
8420 newval
->expr
= expression_planner(defval
);
8421 newval
->is_generated
= true;
8423 tab
->newvals
= lappend(tab
->newvals
, newval
);
8424 tab
->rewrite
|= AT_REWRITE_DEFAULT_VAL
;
8426 /* Drop any pg_statistic entry for the column */
8427 RemoveStatistics(RelationGetRelid(rel
), attnum
);
8429 InvokeObjectPostAlterHook(RelationRelationId
,
8430 RelationGetRelid(rel
), attnum
);
8432 ObjectAddressSubSet(address
, RelationRelationId
,
8433 RelationGetRelid(rel
), attnum
);
8438 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8441 ATPrepDropExpression(Relation rel
, AlterTableCmd
*cmd
, bool recurse
, bool recursing
, LOCKMODE lockmode
)
8444 * Reject ONLY if there are child tables. We could implement this, but it
8445 * is a bit complicated. GENERATED clauses must be attached to the column
8446 * definition and cannot be added later like DEFAULT, so if a child table
8447 * has a generation expression that the parent does not have, the child
8448 * column will necessarily be an attislocal column. So to implement ONLY
8449 * here, we'd need extra code to update attislocal of the direct child
8450 * tables, somewhat similar to how DROP COLUMN does it, so that the
8451 * resulting state can be properly dumped and restored.
8454 find_inheritance_children(RelationGetRelid(rel
), lockmode
))
8456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8457 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8460 * Cannot drop generation expression from inherited columns.
8465 Form_pg_attribute attTup
;
8467 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), cmd
->name
);
8468 if (!HeapTupleIsValid(tuple
))
8470 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8471 errmsg("column \"%s\" of relation \"%s\" does not exist",
8472 cmd
->name
, RelationGetRelationName(rel
))));
8474 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8476 if (attTup
->attinhcount
> 0)
8478 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
8479 errmsg("cannot drop generation expression from inherited column")));
8484 * Return the address of the affected column.
8486 static ObjectAddress
8487 ATExecDropExpression(Relation rel
, const char *colName
, bool missing_ok
, LOCKMODE lockmode
)
8490 Form_pg_attribute attTup
;
8492 Relation attrelation
;
8494 ObjectAddress address
;
8496 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8497 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8498 if (!HeapTupleIsValid(tuple
))
8500 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8501 errmsg("column \"%s\" of relation \"%s\" does not exist",
8502 colName
, RelationGetRelationName(rel
))));
8504 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8505 attnum
= attTup
->attnum
;
8509 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8510 errmsg("cannot alter system column \"%s\"",
8513 if (attTup
->attgenerated
!= ATTRIBUTE_GENERATED_STORED
)
8517 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
8518 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8519 colName
, RelationGetRelationName(rel
))));
8523 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8524 colName
, RelationGetRelationName(rel
))));
8525 heap_freetuple(tuple
);
8526 table_close(attrelation
, RowExclusiveLock
);
8527 return InvalidObjectAddress
;
8532 * Mark the column as no longer generated. (The atthasdef flag needs to
8533 * get cleared too, but RemoveAttrDefault will handle that.)
8535 attTup
->attgenerated
= '\0';
8536 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8538 InvokeObjectPostAlterHook(RelationRelationId
,
8539 RelationGetRelid(rel
),
8541 heap_freetuple(tuple
);
8543 table_close(attrelation
, RowExclusiveLock
);
8546 * Drop the dependency records of the GENERATED expression, in particular
8547 * its INTERNAL dependency on the column, which would otherwise cause
8548 * dependency.c to refuse to perform the deletion.
8550 attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
8551 if (!OidIsValid(attrdefoid
))
8552 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
8553 RelationGetRelid(rel
), attnum
);
8554 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
8556 /* Make above changes visible */
8557 CommandCounterIncrement();
8560 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8561 * safety, but at present we do not expect anything to depend on the
8564 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
,
8567 ObjectAddressSubSet(address
, RelationRelationId
,
8568 RelationGetRelid(rel
), attnum
);
8573 * ALTER TABLE ALTER COLUMN SET STATISTICS
8575 * Return value is the address of the modified column
8577 static ObjectAddress
8578 ATExecSetStatistics(Relation rel
, const char *colName
, int16 colNum
, Node
*newValue
, LOCKMODE lockmode
)
8581 bool newtarget_default
;
8582 Relation attrelation
;
8585 Form_pg_attribute attrtuple
;
8587 ObjectAddress address
;
8588 Datum repl_val
[Natts_pg_attribute
];
8589 bool repl_null
[Natts_pg_attribute
];
8590 bool repl_repl
[Natts_pg_attribute
];
8593 * We allow referencing columns by numbers only for indexes, since table
8594 * column numbers could contain gaps if columns are later dropped.
8596 if (rel
->rd_rel
->relkind
!= RELKIND_INDEX
&&
8597 rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
8600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8601 errmsg("cannot refer to non-index column by number")));
8603 /* -1 was used in previous versions for the default setting */
8604 if (newValue
&& intVal(newValue
) != -1)
8606 newtarget
= intVal(newValue
);
8607 newtarget_default
= false;
8610 newtarget_default
= true;
8612 if (!newtarget_default
)
8615 * Limit target to a sane range
8620 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8621 errmsg("statistics target %d is too low",
8624 else if (newtarget
> MAX_STATISTICS_TARGET
)
8626 newtarget
= MAX_STATISTICS_TARGET
;
8628 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
8629 errmsg("lowering statistics target to %d",
8634 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8638 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8640 if (!HeapTupleIsValid(tuple
))
8642 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8643 errmsg("column \"%s\" of relation \"%s\" does not exist",
8644 colName
, RelationGetRelationName(rel
))));
8648 tuple
= SearchSysCacheAttNum(RelationGetRelid(rel
), colNum
);
8650 if (!HeapTupleIsValid(tuple
))
8652 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8653 errmsg("column number %d of relation \"%s\" does not exist",
8654 colNum
, RelationGetRelationName(rel
))));
8657 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8659 attnum
= attrtuple
->attnum
;
8662 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8663 errmsg("cannot alter system column \"%s\"",
8666 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
||
8667 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
)
8669 if (attnum
> rel
->rd_index
->indnkeyatts
)
8671 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8672 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8673 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
))));
8674 else if (rel
->rd_index
->indkey
.values
[attnum
- 1] != 0)
8676 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8677 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8678 NameStr(attrtuple
->attname
), RelationGetRelationName(rel
)),
8679 errhint("Alter statistics on table column instead.")));
8682 /* Build new tuple. */
8683 memset(repl_null
, false, sizeof(repl_null
));
8684 memset(repl_repl
, false, sizeof(repl_repl
));
8685 if (!newtarget_default
)
8686 repl_val
[Anum_pg_attribute_attstattarget
- 1] = newtarget
;
8688 repl_null
[Anum_pg_attribute_attstattarget
- 1] = true;
8689 repl_repl
[Anum_pg_attribute_attstattarget
- 1] = true;
8690 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8691 repl_val
, repl_null
, repl_repl
);
8692 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, newtuple
);
8694 InvokeObjectPostAlterHook(RelationRelationId
,
8695 RelationGetRelid(rel
),
8697 ObjectAddressSubSet(address
, RelationRelationId
,
8698 RelationGetRelid(rel
), attnum
);
8700 heap_freetuple(newtuple
);
8702 ReleaseSysCache(tuple
);
8704 table_close(attrelation
, RowExclusiveLock
);
8710 * Return value is the address of the modified column
8712 static ObjectAddress
8713 ATExecSetOptions(Relation rel
, const char *colName
, Node
*options
,
8714 bool isReset
, LOCKMODE lockmode
)
8716 Relation attrelation
;
8719 Form_pg_attribute attrtuple
;
8724 ObjectAddress address
;
8725 Datum repl_val
[Natts_pg_attribute
];
8726 bool repl_null
[Natts_pg_attribute
];
8727 bool repl_repl
[Natts_pg_attribute
];
8729 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8731 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8733 if (!HeapTupleIsValid(tuple
))
8735 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8736 errmsg("column \"%s\" of relation \"%s\" does not exist",
8737 colName
, RelationGetRelationName(rel
))));
8738 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8740 attnum
= attrtuple
->attnum
;
8743 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8744 errmsg("cannot alter system column \"%s\"",
8747 /* Generate new proposed attoptions (text array) */
8748 datum
= SysCacheGetAttr(ATTNAME
, tuple
, Anum_pg_attribute_attoptions
,
8750 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
8751 castNode(List
, options
), NULL
, NULL
,
8753 /* Validate new options */
8754 (void) attribute_reloptions(newOptions
, true);
8756 /* Build new tuple. */
8757 memset(repl_null
, false, sizeof(repl_null
));
8758 memset(repl_repl
, false, sizeof(repl_repl
));
8759 if (newOptions
!= (Datum
) 0)
8760 repl_val
[Anum_pg_attribute_attoptions
- 1] = newOptions
;
8762 repl_null
[Anum_pg_attribute_attoptions
- 1] = true;
8763 repl_repl
[Anum_pg_attribute_attoptions
- 1] = true;
8764 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrelation
),
8765 repl_val
, repl_null
, repl_repl
);
8767 /* Update system catalog. */
8768 CatalogTupleUpdate(attrelation
, &newtuple
->t_self
, newtuple
);
8770 InvokeObjectPostAlterHook(RelationRelationId
,
8771 RelationGetRelid(rel
),
8773 ObjectAddressSubSet(address
, RelationRelationId
,
8774 RelationGetRelid(rel
), attnum
);
8776 heap_freetuple(newtuple
);
8778 ReleaseSysCache(tuple
);
8780 table_close(attrelation
, RowExclusiveLock
);
8786 * Helper function for ATExecSetStorage and ATExecSetCompression
8788 * Set the attstorage and/or attcompression fields for index columns
8789 * associated with the specified table column.
8792 SetIndexStorageProperties(Relation rel
, Relation attrelation
,
8794 bool setstorage
, char newstorage
,
8795 bool setcompression
, char newcompression
,
8800 foreach(lc
, RelationGetIndexList(rel
))
8802 Oid indexoid
= lfirst_oid(lc
);
8804 AttrNumber indattnum
= 0;
8807 indrel
= index_open(indexoid
, lockmode
);
8809 for (int i
= 0; i
< indrel
->rd_index
->indnatts
; i
++)
8811 if (indrel
->rd_index
->indkey
.values
[i
] == attnum
)
8820 index_close(indrel
, lockmode
);
8824 tuple
= SearchSysCacheCopyAttNum(RelationGetRelid(indrel
), indattnum
);
8826 if (HeapTupleIsValid(tuple
))
8828 Form_pg_attribute attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8831 attrtuple
->attstorage
= newstorage
;
8834 attrtuple
->attcompression
= newcompression
;
8836 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8838 InvokeObjectPostAlterHook(RelationRelationId
,
8839 RelationGetRelid(rel
),
8842 heap_freetuple(tuple
);
8845 index_close(indrel
, lockmode
);
8850 * ALTER TABLE ALTER COLUMN SET STORAGE
8852 * Return value is the address of the modified column
8854 static ObjectAddress
8855 ATExecSetStorage(Relation rel
, const char *colName
, Node
*newValue
, LOCKMODE lockmode
)
8857 Relation attrelation
;
8859 Form_pg_attribute attrtuple
;
8861 ObjectAddress address
;
8863 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
8865 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
8867 if (!HeapTupleIsValid(tuple
))
8869 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8870 errmsg("column \"%s\" of relation \"%s\" does not exist",
8871 colName
, RelationGetRelationName(rel
))));
8872 attrtuple
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8874 attnum
= attrtuple
->attnum
;
8877 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
8878 errmsg("cannot alter system column \"%s\"",
8881 attrtuple
->attstorage
= GetAttributeStorage(attrtuple
->atttypid
, strVal(newValue
));
8883 CatalogTupleUpdate(attrelation
, &tuple
->t_self
, tuple
);
8885 InvokeObjectPostAlterHook(RelationRelationId
,
8886 RelationGetRelid(rel
),
8890 * Apply the change to indexes as well (only for simple index columns,
8891 * matching behavior of index.c ConstructTupleDescriptor()).
8893 SetIndexStorageProperties(rel
, attrelation
, attnum
,
8894 true, attrtuple
->attstorage
,
8898 heap_freetuple(tuple
);
8900 table_close(attrelation
, RowExclusiveLock
);
8902 ObjectAddressSubSet(address
, RelationRelationId
,
8903 RelationGetRelid(rel
), attnum
);
8909 * ALTER TABLE DROP COLUMN
8911 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8912 * because we have to decide at runtime whether to recurse or not depending
8913 * on whether attinhcount goes to zero or not. (We can't check this in a
8914 * static pre-pass because it won't handle multiple inheritance situations
8918 ATPrepDropColumn(List
**wqueue
, Relation rel
, bool recurse
, bool recursing
,
8919 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
8920 AlterTableUtilityContext
*context
)
8922 if (rel
->rd_rel
->reloftype
&& !recursing
)
8924 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
8925 errmsg("cannot drop column from typed table")));
8927 if (rel
->rd_rel
->relkind
== RELKIND_COMPOSITE_TYPE
)
8928 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
8931 cmd
->recurse
= true;
8935 * Drops column 'colName' from relation 'rel' and returns the address of the
8936 * dropped column. The column is also dropped (or marked as no longer
8937 * inherited from relation) from the relation's inheritance children, if any.
8939 * In the recursive invocations for inheritance child relations, instead of
8940 * dropping the column directly (if to be dropped at all), its object address
8941 * is added to 'addrs', which must be non-NULL in such invocations. All
8942 * columns are dropped at the same time after all the children have been
8943 * checked recursively.
8945 static ObjectAddress
8946 ATExecDropColumn(List
**wqueue
, Relation rel
, const char *colName
,
8947 DropBehavior behavior
,
8948 bool recurse
, bool recursing
,
8949 bool missing_ok
, LOCKMODE lockmode
,
8950 ObjectAddresses
*addrs
)
8953 Form_pg_attribute targetatt
;
8956 ObjectAddress object
;
8959 /* At top level, permission check was done in ATPrepCmd, else do it */
8961 ATSimplePermissions(AT_DropColumn
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
8963 /* Initialize addrs on the first invocation */
8964 Assert(!recursing
|| addrs
!= NULL
);
8966 /* since this function recurses, it could be driven to stack overflow */
8967 check_stack_depth();
8970 addrs
= new_object_addresses();
8973 * get the number of the attribute
8975 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
8976 if (!HeapTupleIsValid(tuple
))
8981 (errcode(ERRCODE_UNDEFINED_COLUMN
),
8982 errmsg("column \"%s\" of relation \"%s\" does not exist",
8983 colName
, RelationGetRelationName(rel
))));
8988 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8989 colName
, RelationGetRelationName(rel
))));
8990 return InvalidObjectAddress
;
8993 targetatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
8995 attnum
= targetatt
->attnum
;
8997 /* Can't drop a system attribute */
9000 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9001 errmsg("cannot drop system column \"%s\"",
9005 * Don't drop inherited columns, unless recursing (presumably from a drop
9006 * of the parent column)
9008 if (targetatt
->attinhcount
> 0 && !recursing
)
9010 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9011 errmsg("cannot drop inherited column \"%s\"",
9015 * Don't drop columns used in the partition key, either. (If we let this
9016 * go through, the key column's dependencies would cause a cascaded drop
9017 * of the whole table, which is surely not what the user expected.)
9019 if (has_partition_attrs(rel
,
9020 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
9023 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9024 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9025 colName
, RelationGetRelationName(rel
))));
9027 ReleaseSysCache(tuple
);
9030 * Propagate to children as appropriate. Unlike most other ALTER
9031 * routines, we have to do this one level of recursion at a time; we can't
9032 * use find_all_inheritors to do it in one pass.
9035 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9043 * In case of a partitioned table, the column must be dropped from the
9044 * partitions as well.
9046 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&& !recurse
)
9048 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9049 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9050 errhint("Do not specify the ONLY keyword.")));
9052 attr_rel
= table_open(AttributeRelationId
, RowExclusiveLock
);
9053 foreach(child
, children
)
9055 Oid childrelid
= lfirst_oid(child
);
9057 Form_pg_attribute childatt
;
9059 /* find_inheritance_children already got lock */
9060 childrel
= table_open(childrelid
, NoLock
);
9061 CheckAlterTableIsSafe(childrel
);
9063 tuple
= SearchSysCacheCopyAttName(childrelid
, colName
);
9064 if (!HeapTupleIsValid(tuple
)) /* shouldn't happen */
9065 elog(ERROR
, "cache lookup failed for attribute \"%s\" of relation %u",
9066 colName
, childrelid
);
9067 childatt
= (Form_pg_attribute
) GETSTRUCT(tuple
);
9069 if (childatt
->attinhcount
<= 0) /* shouldn't happen */
9070 elog(ERROR
, "relation %u has non-inherited attribute \"%s\"",
9071 childrelid
, colName
);
9076 * If the child column has other definition sources, just
9077 * decrement its inheritance count; if not, recurse to delete
9080 if (childatt
->attinhcount
== 1 && !childatt
->attislocal
)
9082 /* Time to delete this child column, too */
9083 ATExecDropColumn(wqueue
, childrel
, colName
,
9084 behavior
, true, true,
9085 false, lockmode
, addrs
);
9089 /* Child column must survive my deletion */
9090 childatt
->attinhcount
--;
9092 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9094 /* Make update visible */
9095 CommandCounterIncrement();
9101 * If we were told to drop ONLY in this table (no recursion),
9102 * we need to mark the inheritors' attributes as locally
9103 * defined rather than inherited.
9105 childatt
->attinhcount
--;
9106 childatt
->attislocal
= true;
9108 CatalogTupleUpdate(attr_rel
, &tuple
->t_self
, tuple
);
9110 /* Make update visible */
9111 CommandCounterIncrement();
9114 heap_freetuple(tuple
);
9116 table_close(childrel
, NoLock
);
9118 table_close(attr_rel
, RowExclusiveLock
);
9121 /* Add object to delete */
9122 object
.classId
= RelationRelationId
;
9123 object
.objectId
= RelationGetRelid(rel
);
9124 object
.objectSubId
= attnum
;
9125 add_exact_object_address(&object
, addrs
);
9129 /* Recursion has ended, drop everything that was collected */
9130 performMultipleDeletions(addrs
, behavior
, 0);
9131 free_object_addresses(addrs
);
9138 * ALTER TABLE ADD INDEX
9140 * There is no such command in the grammar, but parse_utilcmd.c converts
9141 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9142 * us schedule creation of the index at the appropriate time during ALTER.
9144 * Return value is the address of the new index.
9146 static ObjectAddress
9147 ATExecAddIndex(AlteredTableInfo
*tab
, Relation rel
,
9148 IndexStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9153 ObjectAddress address
;
9155 Assert(IsA(stmt
, IndexStmt
));
9156 Assert(!stmt
->concurrent
);
9158 /* The IndexStmt has already been through transformIndexStmt */
9159 Assert(stmt
->transformed
);
9161 /* suppress schema rights check when rebuilding existing index */
9162 check_rights
= !is_rebuild
;
9163 /* skip index build if phase 3 will do it or we're reusing an old one */
9164 skip_build
= tab
->rewrite
> 0 || RelFileNumberIsValid(stmt
->oldNumber
);
9165 /* suppress notices when rebuilding existing index */
9168 address
= DefineIndex(RelationGetRelid(rel
),
9170 InvalidOid
, /* no predefined OID */
9171 InvalidOid
, /* no parent index */
9172 InvalidOid
, /* no parent constraint */
9173 -1, /* total_parts unknown */
9174 true, /* is_alter_table */
9176 false, /* check_not_in_use - we did it already */
9181 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9182 * new index instead of building from scratch. Restore associated fields.
9183 * This may store InvalidSubTransactionId in both fields, in which case
9184 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9185 * this after the CCI that made catalog rows visible to any rebuild. The
9186 * DROP of the old edition of this index will have scheduled the storage
9187 * for deletion at commit, so cancel that pending deletion.
9189 if (RelFileNumberIsValid(stmt
->oldNumber
))
9191 Relation irel
= index_open(address
.objectId
, NoLock
);
9193 irel
->rd_createSubid
= stmt
->oldCreateSubid
;
9194 irel
->rd_firstRelfilelocatorSubid
= stmt
->oldFirstRelfilelocatorSubid
;
9195 RelationPreserveStorage(irel
->rd_locator
, true);
9196 index_close(irel
, NoLock
);
9203 * ALTER TABLE ADD STATISTICS
9205 * This is no such command in the grammar, but we use this internally to add
9206 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9207 * column type change.
9209 static ObjectAddress
9210 ATExecAddStatistics(AlteredTableInfo
*tab
, Relation rel
,
9211 CreateStatsStmt
*stmt
, bool is_rebuild
, LOCKMODE lockmode
)
9213 ObjectAddress address
;
9215 Assert(IsA(stmt
, CreateStatsStmt
));
9217 /* The CreateStatsStmt has already been through transformStatsStmt */
9218 Assert(stmt
->transformed
);
9220 address
= CreateStatistics(stmt
);
9226 * ALTER TABLE ADD CONSTRAINT USING INDEX
9228 * Returns the address of the new constraint.
9230 static ObjectAddress
9231 ATExecAddIndexConstraint(AlteredTableInfo
*tab
, Relation rel
,
9232 IndexStmt
*stmt
, LOCKMODE lockmode
)
9234 Oid index_oid
= stmt
->indexOid
;
9237 IndexInfo
*indexInfo
;
9238 char *constraintName
;
9239 char constraintType
;
9240 ObjectAddress address
;
9243 Assert(IsA(stmt
, IndexStmt
));
9244 Assert(OidIsValid(index_oid
));
9245 Assert(stmt
->isconstraint
);
9248 * Doing this on partitioned tables is not a simple feature to implement,
9249 * so let's punt for now.
9251 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9253 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
9254 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9256 indexRel
= index_open(index_oid
, AccessShareLock
);
9258 indexName
= pstrdup(RelationGetRelationName(indexRel
));
9260 indexInfo
= BuildIndexInfo(indexRel
);
9262 /* this should have been checked at parse time */
9263 if (!indexInfo
->ii_Unique
)
9264 elog(ERROR
, "index \"%s\" is not unique", indexName
);
9267 * Determine name to assign to constraint. We require a constraint to
9268 * have the same name as the underlying index; therefore, use the index's
9269 * existing name as the default constraint name, and if the user
9270 * explicitly gives some other name for the constraint, rename the index
9273 constraintName
= stmt
->idxname
;
9274 if (constraintName
== NULL
)
9275 constraintName
= indexName
;
9276 else if (strcmp(constraintName
, indexName
) != 0)
9279 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9280 indexName
, constraintName
)));
9281 RenameRelationInternal(index_oid
, constraintName
, false, true);
9284 /* Extra checks needed if making primary key */
9286 index_check_primary_key(rel
, indexInfo
, true, stmt
);
9288 /* Note we currently don't support EXCLUSION constraints here */
9290 constraintType
= CONSTRAINT_PRIMARY
;
9292 constraintType
= CONSTRAINT_UNIQUE
;
9294 /* Create the catalog entries for the constraint */
9295 flags
= INDEX_CONSTR_CREATE_UPDATE_INDEX
|
9296 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
|
9297 (stmt
->initdeferred
? INDEX_CONSTR_CREATE_INIT_DEFERRED
: 0) |
9298 (stmt
->deferrable
? INDEX_CONSTR_CREATE_DEFERRABLE
: 0) |
9299 (stmt
->primary
? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
: 0);
9301 address
= index_constraint_create(rel
,
9308 allowSystemTableMods
,
9309 false); /* is_internal */
9311 index_close(indexRel
, NoLock
);
9317 * ALTER TABLE ADD CONSTRAINT
9319 * Return value is the address of the new constraint; if no constraint was
9320 * added, InvalidObjectAddress is returned.
9322 static ObjectAddress
9323 ATExecAddConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9324 Constraint
*newConstraint
, bool recurse
, bool is_readd
,
9327 ObjectAddress address
= InvalidObjectAddress
;
9329 Assert(IsA(newConstraint
, Constraint
));
9332 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
9333 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
9334 * switch anyway to make it easier to add more code later.
9336 switch (newConstraint
->contype
)
9340 ATAddCheckConstraint(wqueue
, tab
, rel
,
9341 newConstraint
, recurse
, false, is_readd
,
9345 case CONSTR_FOREIGN
:
9348 * Assign or validate constraint name
9350 if (newConstraint
->conname
)
9352 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
9353 RelationGetRelid(rel
),
9354 newConstraint
->conname
))
9356 (errcode(ERRCODE_DUPLICATE_OBJECT
),
9357 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9358 newConstraint
->conname
,
9359 RelationGetRelationName(rel
))));
9362 newConstraint
->conname
=
9363 ChooseConstraintName(RelationGetRelationName(rel
),
9364 ChooseForeignKeyConstraintNameAddition(newConstraint
->fk_attrs
),
9366 RelationGetNamespace(rel
),
9369 address
= ATAddForeignKeyConstraint(wqueue
, tab
, rel
,
9376 elog(ERROR
, "unrecognized constraint type: %d",
9377 (int) newConstraint
->contype
);
9384 * Generate the column-name portion of the constraint name for a new foreign
9385 * key given the list of column names that reference the referenced
9386 * table. This will be passed to ChooseConstraintName along with the parent
9387 * table name and the "fkey" suffix.
9389 * We know that less than NAMEDATALEN characters will actually be used, so we
9390 * can truncate the result once we've generated that many.
9392 * XXX see also ChooseExtendedStatisticNameAddition and
9393 * ChooseIndexNameAddition.
9396 ChooseForeignKeyConstraintNameAddition(List
*colnames
)
9398 char buf
[NAMEDATALEN
* 2];
9403 foreach(lc
, colnames
)
9405 const char *name
= strVal(lfirst(lc
));
9408 buf
[buflen
++] = '_'; /* insert _ between names */
9411 * At this point we have buflen <= NAMEDATALEN. name should be less
9412 * than NAMEDATALEN already, but use strlcpy for paranoia.
9414 strlcpy(buf
+ buflen
, name
, NAMEDATALEN
);
9415 buflen
+= strlen(buf
+ buflen
);
9416 if (buflen
>= NAMEDATALEN
)
9419 return pstrdup(buf
);
9423 * Add a check constraint to a single table and its children. Returns the
9424 * address of the constraint added to the parent relation, if one gets added,
9425 * or InvalidObjectAddress otherwise.
9427 * Subroutine for ATExecAddConstraint.
9429 * We must recurse to child tables during execution, rather than using
9430 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9431 * constraints *must* be given the same name, else they won't be seen as
9432 * related later. If the user didn't explicitly specify a name, then
9433 * AddRelationNewConstraints would normally assign different names to the
9434 * child constraints. To fix that, we must capture the name assigned at
9435 * the parent table and pass that down.
9437 static ObjectAddress
9438 ATAddCheckConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9439 Constraint
*constr
, bool recurse
, bool recursing
,
9440 bool is_readd
, LOCKMODE lockmode
)
9446 ObjectAddress address
= InvalidObjectAddress
;
9448 /* At top level, permission check was done in ATPrepCmd, else do it */
9450 ATSimplePermissions(AT_AddConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
9453 * Call AddRelationNewConstraints to do the work, making sure it works on
9454 * a copy of the Constraint so transformExpr can't modify the original. It
9455 * returns a list of cooked constraints.
9457 * If the constraint ends up getting merged with a pre-existing one, it's
9458 * omitted from the returned list, which is what we want: we do not need
9459 * to do any validation work. That can only happen at child tables,
9460 * though, since we disallow merging at the top level.
9462 newcons
= AddRelationNewConstraints(rel
, NIL
,
9463 list_make1(copyObject(constr
)),
9464 recursing
|| is_readd
, /* allow_merge */
9465 !recursing
, /* is_local */
9466 is_readd
, /* is_internal */
9467 NULL
); /* queryString not available
9470 /* we don't expect more than one constraint here */
9471 Assert(list_length(newcons
) <= 1);
9473 /* Add each to-be-validated constraint to Phase 3's queue */
9474 foreach(lcon
, newcons
)
9476 CookedConstraint
*ccon
= (CookedConstraint
*) lfirst(lcon
);
9478 if (!ccon
->skip_validation
)
9480 NewConstraint
*newcon
;
9482 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
9483 newcon
->name
= ccon
->name
;
9484 newcon
->contype
= ccon
->contype
;
9485 newcon
->qual
= ccon
->expr
;
9487 tab
->constraints
= lappend(tab
->constraints
, newcon
);
9490 /* Save the actually assigned name if it was defaulted */
9491 if (constr
->conname
== NULL
)
9492 constr
->conname
= ccon
->name
;
9494 ObjectAddressSet(address
, ConstraintRelationId
, ccon
->conoid
);
9497 /* At this point we must have a locked-down name to use */
9498 Assert(constr
->conname
!= NULL
);
9500 /* Advance command counter in case same table is visited multiple times */
9501 CommandCounterIncrement();
9504 * If the constraint got merged with an existing constraint, we're done.
9505 * We mustn't recurse to child tables in this case, because they've
9506 * already got the constraint, and visiting them again would lead to an
9507 * incorrect value for coninhcount.
9513 * If adding a NO INHERIT constraint, no need to find our children.
9515 if (constr
->is_no_inherit
)
9519 * Propagate to children as appropriate. Unlike most other ALTER
9520 * routines, we have to do this one level of recursion at a time; we can't
9521 * use find_all_inheritors to do it in one pass.
9524 find_inheritance_children(RelationGetRelid(rel
), lockmode
);
9527 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9528 * constraint creation only if there are no children currently. Error out
9531 if (!recurse
&& children
!= NIL
)
9533 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9534 errmsg("constraint must be added to child tables too")));
9536 foreach(child
, children
)
9538 Oid childrelid
= lfirst_oid(child
);
9540 AlteredTableInfo
*childtab
;
9542 /* find_inheritance_children already got lock */
9543 childrel
= table_open(childrelid
, NoLock
);
9544 CheckAlterTableIsSafe(childrel
);
9546 /* Find or create work queue entry for this table */
9547 childtab
= ATGetQueueEntry(wqueue
, childrel
);
9549 /* Recurse to child */
9550 ATAddCheckConstraint(wqueue
, childtab
, childrel
,
9551 constr
, recurse
, true, is_readd
, lockmode
);
9553 table_close(childrel
, NoLock
);
9560 * Add a foreign-key constraint to a single table; return the new constraint's
9563 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9564 * lock on the rel, and have done appropriate validity checks for it.
9565 * We do permissions checks here, however.
9567 * When the referenced or referencing tables (or both) are partitioned,
9568 * multiple pg_constraint rows are required -- one for each partitioned table
9569 * and each partition on each side (fortunately, not one for every combination
9570 * thereof). We also need action triggers on each leaf partition on the
9571 * referenced side, and check triggers on each leaf partition on the
9574 static ObjectAddress
9575 ATAddForeignKeyConstraint(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
9576 Constraint
*fkconstraint
,
9577 bool recurse
, bool recursing
, LOCKMODE lockmode
)
9580 int16 pkattnum
[INDEX_MAX_KEYS
] = {0};
9581 int16 fkattnum
[INDEX_MAX_KEYS
] = {0};
9582 Oid pktypoid
[INDEX_MAX_KEYS
] = {0};
9583 Oid fktypoid
[INDEX_MAX_KEYS
] = {0};
9584 Oid opclasses
[INDEX_MAX_KEYS
] = {0};
9585 Oid pfeqoperators
[INDEX_MAX_KEYS
] = {0};
9586 Oid ppeqoperators
[INDEX_MAX_KEYS
] = {0};
9587 Oid ffeqoperators
[INDEX_MAX_KEYS
] = {0};
9588 int16 fkdelsetcols
[INDEX_MAX_KEYS
] = {0};
9595 ObjectAddress address
;
9596 ListCell
*old_pfeqop_item
= list_head(fkconstraint
->old_conpfeqop
);
9599 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9600 * delete rows out from under us.
9602 if (OidIsValid(fkconstraint
->old_pktable_oid
))
9603 pkrel
= table_open(fkconstraint
->old_pktable_oid
, ShareRowExclusiveLock
);
9605 pkrel
= table_openrv(fkconstraint
->pktable
, ShareRowExclusiveLock
);
9608 * Validity checks (permission checks wait till we have the column
9611 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
9615 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9616 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9617 RelationGetRelationName(rel
),
9618 RelationGetRelationName(pkrel
))));
9619 if (fkconstraint
->skip_validation
&& !fkconstraint
->initially_valid
)
9621 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9622 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9623 RelationGetRelationName(rel
),
9624 RelationGetRelationName(pkrel
)),
9625 errdetail("This feature is not yet supported on partitioned tables.")));
9628 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
9629 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
9631 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
9632 errmsg("referenced relation \"%s\" is not a table",
9633 RelationGetRelationName(pkrel
))));
9635 if (!allowSystemTableMods
&& IsSystemRelation(pkrel
))
9637 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
9638 errmsg("permission denied: \"%s\" is a system catalog",
9639 RelationGetRelationName(pkrel
))));
9642 * References from permanent or unlogged tables to temp tables, and from
9643 * permanent tables to unlogged tables, are disallowed because the
9644 * referenced data can vanish out from under us. References from temp
9645 * tables to any other table type are also disallowed, because other
9646 * backends might need to run the RI triggers on the perm table, but they
9647 * can't reliably see tuples in the local buffers of other backends.
9649 switch (rel
->rd_rel
->relpersistence
)
9651 case RELPERSISTENCE_PERMANENT
:
9652 if (!RelationIsPermanent(pkrel
))
9654 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9655 errmsg("constraints on permanent tables may reference only permanent tables")));
9657 case RELPERSISTENCE_UNLOGGED
:
9658 if (!RelationIsPermanent(pkrel
)
9659 && pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_UNLOGGED
)
9661 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9662 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9664 case RELPERSISTENCE_TEMP
:
9665 if (pkrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
9667 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9668 errmsg("constraints on temporary tables may reference only temporary tables")));
9669 if (!pkrel
->rd_islocaltemp
|| !rel
->rd_islocaltemp
)
9671 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
9672 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9677 * Look up the referencing attributes to make sure they exist, and record
9678 * their attnums and type OIDs.
9680 numfks
= transformColumnNameList(RelationGetRelid(rel
),
9681 fkconstraint
->fk_attrs
,
9682 fkattnum
, fktypoid
);
9684 numfkdelsetcols
= transformColumnNameList(RelationGetRelid(rel
),
9685 fkconstraint
->fk_del_set_cols
,
9686 fkdelsetcols
, NULL
);
9687 validateFkOnDeleteSetColumns(numfks
, fkattnum
,
9688 numfkdelsetcols
, fkdelsetcols
,
9689 fkconstraint
->fk_del_set_cols
);
9692 * If the attribute list for the referenced table was omitted, lookup the
9693 * definition of the primary key and use it. Otherwise, validate the
9694 * supplied attribute list. In either case, discover the index OID and
9695 * index opclasses, and the attnums and type OIDs of the attributes.
9697 if (fkconstraint
->pk_attrs
== NIL
)
9699 numpks
= transformFkeyGetPrimaryKey(pkrel
, &indexOid
,
9700 &fkconstraint
->pk_attrs
,
9706 numpks
= transformColumnNameList(RelationGetRelid(pkrel
),
9707 fkconstraint
->pk_attrs
,
9708 pkattnum
, pktypoid
);
9709 /* Look for an index matching the column list */
9710 indexOid
= transformFkeyCheckAttrs(pkrel
, numpks
, pkattnum
,
9715 * Now we can check permissions.
9717 checkFkeyPermissions(pkrel
, pkattnum
, numpks
);
9720 * Check some things for generated columns.
9722 for (i
= 0; i
< numfks
; i
++)
9724 char attgenerated
= TupleDescAttr(RelationGetDescr(rel
), fkattnum
[i
] - 1)->attgenerated
;
9729 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9731 if (fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETNULL
||
9732 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_SETDEFAULT
||
9733 fkconstraint
->fk_upd_action
== FKCONSTR_ACTION_CASCADE
)
9735 (errcode(ERRCODE_SYNTAX_ERROR
),
9736 errmsg("invalid %s action for foreign key constraint containing generated column",
9738 if (fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETNULL
||
9739 fkconstraint
->fk_del_action
== FKCONSTR_ACTION_SETDEFAULT
)
9741 (errcode(ERRCODE_SYNTAX_ERROR
),
9742 errmsg("invalid %s action for foreign key constraint containing generated column",
9748 * Look up the equality operators to use in the constraint.
9750 * Note that we have to be careful about the difference between the actual
9751 * PK column type and the opclass' declared input type, which might be
9752 * only binary-compatible with it. The declared opcintype is the right
9753 * thing to probe pg_amop with.
9755 if (numfks
!= numpks
)
9757 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
9758 errmsg("number of referencing and referenced columns for foreign key disagree")));
9761 * On the strength of a previous constraint, we might avoid scanning
9762 * tables to validate this one. See below.
9764 old_check_ok
= (fkconstraint
->old_conpfeqop
!= NIL
);
9765 Assert(!old_check_ok
|| numfks
== list_length(fkconstraint
->old_conpfeqop
));
9767 for (i
= 0; i
< numpks
; i
++)
9769 Oid pktype
= pktypoid
[i
];
9770 Oid fktype
= fktypoid
[i
];
9773 Form_pg_opclass cla_tup
;
9783 /* We need several fields out of the pg_opclass entry */
9784 cla_ht
= SearchSysCache1(CLAOID
, ObjectIdGetDatum(opclasses
[i
]));
9785 if (!HeapTupleIsValid(cla_ht
))
9786 elog(ERROR
, "cache lookup failed for opclass %u", opclasses
[i
]);
9787 cla_tup
= (Form_pg_opclass
) GETSTRUCT(cla_ht
);
9788 amid
= cla_tup
->opcmethod
;
9789 opfamily
= cla_tup
->opcfamily
;
9790 opcintype
= cla_tup
->opcintype
;
9791 ReleaseSysCache(cla_ht
);
9794 * Check it's a btree; currently this can never fail since no other
9795 * index AMs support unique indexes. If we ever did have other types
9796 * of unique indexes, we'd need a way to determine which operator
9797 * strategy number is equality. (Is it reasonable to insist that
9798 * every such index AM use btree's number for equality?)
9800 if (amid
!= BTREE_AM_OID
)
9801 elog(ERROR
, "only b-tree indexes are supported for foreign keys");
9802 eqstrategy
= BTEqualStrategyNumber
;
9805 * There had better be a primary equality operator for the index.
9806 * We'll use it for PK = PK comparisons.
9808 ppeqop
= get_opfamily_member(opfamily
, opcintype
, opcintype
,
9811 if (!OidIsValid(ppeqop
))
9812 elog(ERROR
, "missing operator %d(%u,%u) in opfamily %u",
9813 eqstrategy
, opcintype
, opcintype
, opfamily
);
9816 * Are there equality operators that take exactly the FK type? Assume
9817 * we should look through any domain here.
9819 fktyped
= getBaseType(fktype
);
9821 pfeqop
= get_opfamily_member(opfamily
, opcintype
, fktyped
,
9823 if (OidIsValid(pfeqop
))
9825 pfeqop_right
= fktyped
;
9826 ffeqop
= get_opfamily_member(opfamily
, fktyped
, fktyped
,
9831 /* keep compiler quiet */
9832 pfeqop_right
= InvalidOid
;
9833 ffeqop
= InvalidOid
;
9836 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9839 * Otherwise, look for an implicit cast from the FK type to the
9840 * opcintype, and if found, use the primary equality operator.
9841 * This is a bit tricky because opcintype might be a polymorphic
9842 * type such as ANYARRAY or ANYENUM; so what we have to test is
9843 * whether the two actual column types can be concurrently cast to
9844 * that type. (Otherwise, we'd fail to reject combinations such
9845 * as int[] and point[].)
9847 Oid input_typeids
[2];
9848 Oid target_typeids
[2];
9850 input_typeids
[0] = pktype
;
9851 input_typeids
[1] = fktype
;
9852 target_typeids
[0] = opcintype
;
9853 target_typeids
[1] = opcintype
;
9854 if (can_coerce_type(2, input_typeids
, target_typeids
,
9857 pfeqop
= ffeqop
= ppeqop
;
9858 pfeqop_right
= opcintype
;
9862 if (!(OidIsValid(pfeqop
) && OidIsValid(ffeqop
)))
9864 (errcode(ERRCODE_DATATYPE_MISMATCH
),
9865 errmsg("foreign key constraint \"%s\" cannot be implemented",
9866 fkconstraint
->conname
),
9867 errdetail("Key columns \"%s\" and \"%s\" "
9868 "are of incompatible types: %s and %s.",
9869 strVal(list_nth(fkconstraint
->fk_attrs
, i
)),
9870 strVal(list_nth(fkconstraint
->pk_attrs
, i
)),
9871 format_type_be(fktype
),
9872 format_type_be(pktype
))));
9877 * When a pfeqop changes, revalidate the constraint. We could
9878 * permit intra-opfamily changes, but that adds subtle complexity
9879 * without any concrete benefit for core types. We need not
9880 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9882 old_check_ok
= (pfeqop
== lfirst_oid(old_pfeqop_item
));
9883 old_pfeqop_item
= lnext(fkconstraint
->old_conpfeqop
,
9890 CoercionPathType old_pathtype
;
9891 CoercionPathType new_pathtype
;
9894 Form_pg_attribute attr
= TupleDescAttr(tab
->oldDesc
,
9898 * Identify coercion pathways from each of the old and new FK-side
9899 * column types to the right (foreign) operand type of the pfeqop.
9900 * We may assume that pg_constraint.conkey is not changing.
9902 old_fktype
= attr
->atttypid
;
9903 new_fktype
= fktype
;
9904 old_pathtype
= findFkeyCast(pfeqop_right
, old_fktype
,
9906 new_pathtype
= findFkeyCast(pfeqop_right
, new_fktype
,
9910 * Upon a change to the cast from the FK column to its pfeqop
9911 * operand, revalidate the constraint. For this evaluation, a
9912 * binary coercion cast is equivalent to no cast at all. While
9913 * type implementors should design implicit casts with an eye
9914 * toward consistency of operations like equality, we cannot
9915 * assume here that they have done so.
9917 * A function with a polymorphic argument could change behavior
9918 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9919 * when the cast destination is polymorphic, we only avoid
9920 * revalidation if the input type has not changed at all. Given
9921 * just the core data types and operator classes, this requirement
9922 * prevents no would-be optimizations.
9924 * If the cast converts from a base type to a domain thereon, then
9925 * that domain type must be the opcintype of the unique index.
9926 * Necessarily, the primary key column must then be of the domain
9927 * type. Since the constraint was previously valid, all values on
9928 * the foreign side necessarily exist on the primary side and in
9929 * turn conform to the domain. Consequently, we need not treat
9930 * domains specially here.
9932 * Since we require that all collations share the same notion of
9933 * equality (which they do, because texteq reduces to bitwise
9934 * equality), we don't compare collation here.
9936 * We need not directly consider the PK type. It's necessarily
9937 * binary coercible to the opcintype of the unique index column,
9938 * and ri_triggers.c will only deal with PK datums in terms of
9939 * that opcintype. Changing the opcintype also changes pfeqop.
9941 old_check_ok
= (new_pathtype
== old_pathtype
&&
9942 new_castfunc
== old_castfunc
&&
9943 (!IsPolymorphicType(pfeqop_right
) ||
9944 new_fktype
== old_fktype
));
9947 pfeqoperators
[i
] = pfeqop
;
9948 ppeqoperators
[i
] = ppeqop
;
9949 ffeqoperators
[i
] = ffeqop
;
9953 * Create all the constraint and trigger objects, recursing to partitions
9954 * as necessary. First handle the referenced side.
9956 address
= addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, pkrel
,
9958 InvalidOid
, /* no parent constraint */
9968 InvalidOid
, InvalidOid
);
9970 /* Now handle the referencing side. */
9971 addFkRecurseReferencing(wqueue
, fkconstraint
, rel
, pkrel
,
9984 InvalidOid
, InvalidOid
);
9987 * Done. Close pk table, but keep lock until we've committed.
9989 table_close(pkrel
, NoLock
);
9995 * validateFkOnDeleteSetColumns
9996 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9997 * column lists are valid.
10000 validateFkOnDeleteSetColumns(int numfks
, const int16
*fkattnums
,
10001 int numfksetcols
, const int16
*fksetcolsattnums
,
10004 for (int i
= 0; i
< numfksetcols
; i
++)
10006 int16 setcol_attnum
= fksetcolsattnums
[i
];
10009 for (int j
= 0; j
< numfks
; j
++)
10011 if (fkattnums
[j
] == setcol_attnum
)
10020 char *col
= strVal(list_nth(fksetcols
, i
));
10023 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
10024 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col
)));
10030 * addFkRecurseReferenced
10031 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10032 * side of the constraint
10034 * Create pg_constraint rows for the referenced side of the constraint,
10035 * referencing the parent of the referencing side; also create action triggers
10036 * on leaf partitions. If the table is partitioned, recurse to handle each
10039 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10040 * of an ALTER TABLE sequence.
10041 * fkconstraint is the constraint being added.
10042 * rel is the root referencing relation.
10043 * pkrel is the referenced relation; might be a partition, if recursing.
10044 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10045 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10046 * top-level constraint.
10047 * numfks is the number of columns in the foreign key
10048 * pkattnum is the attnum array of referenced attributes.
10049 * fkattnum is the attnum array of referencing attributes.
10050 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10052 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10053 * NULL/DEFAULT clause
10054 * pf/pp/ffeqoperators are OID array of operators between columns.
10055 * old_check_ok signals that this constraint replaces an existing one that
10056 * was already validated (thus this one doesn't need validation).
10057 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10058 * a partition, are the OIDs of the parent action triggers for DELETE and
10059 * UPDATE respectively.
10061 static ObjectAddress
10062 addFkRecurseReferenced(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10063 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10065 int16
*pkattnum
, int16
*fkattnum
, Oid
*pfeqoperators
,
10066 Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10067 int numfkdelsetcols
, int16
*fkdelsetcols
,
10069 Oid parentDelTrigger
, Oid parentUpdTrigger
)
10071 ObjectAddress address
;
10077 Oid deleteTriggerOid
,
10081 * Verify relkind for each referenced partition. At the top level, this
10082 * is redundant with a previous check, but we need it when recursing.
10084 if (pkrel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
10085 pkrel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
10087 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10088 errmsg("referenced relation \"%s\" is not a table",
10089 RelationGetRelationName(pkrel
))));
10092 * Caller supplies us with a constraint name; however, it may be used in
10093 * this partition, so come up with a different one in that case.
10095 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10096 RelationGetRelid(rel
),
10097 fkconstraint
->conname
))
10098 conname
= ChooseConstraintName(RelationGetRelationName(rel
),
10099 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10101 RelationGetNamespace(rel
), NIL
);
10103 conname
= fkconstraint
->conname
;
10105 if (OidIsValid(parentConstr
))
10107 conislocal
= false;
10109 connoinherit
= false;
10117 * always inherit for partitioned tables, never for legacy inheritance
10119 connoinherit
= rel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
;
10123 * Record the FK constraint in pg_constraint.
10125 constrOid
= CreateConstraintEntry(conname
,
10126 RelationGetNamespace(rel
),
10127 CONSTRAINT_FOREIGN
,
10128 fkconstraint
->deferrable
,
10129 fkconstraint
->initdeferred
,
10130 fkconstraint
->initially_valid
,
10132 RelationGetRelid(rel
),
10136 InvalidOid
, /* not a domain constraint */
10138 RelationGetRelid(pkrel
),
10144 fkconstraint
->fk_upd_action
,
10145 fkconstraint
->fk_del_action
,
10148 fkconstraint
->fk_matchtype
,
10149 NULL
, /* no exclusion constraint */
10150 NULL
, /* no check constraint */
10152 conislocal
, /* islocal */
10153 coninhcount
, /* inhcount */
10154 connoinherit
, /* conNoInherit */
10155 false); /* is_internal */
10157 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10160 * Mark the child constraint as part of the parent constraint; it must not
10161 * be dropped on its own. (This constraint is deleted when the partition
10162 * is detached, but a special check needs to occur that the partition
10163 * contains no referenced values.)
10165 if (OidIsValid(parentConstr
))
10167 ObjectAddress referenced
;
10169 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10170 recordDependencyOn(&address
, &referenced
, DEPENDENCY_INTERNAL
);
10173 /* make new constraint visible, in case we add more */
10174 CommandCounterIncrement();
10177 * Create the action triggers that enforce the constraint.
10179 createForeignKeyActionTriggers(rel
, RelationGetRelid(pkrel
),
10181 constrOid
, indexOid
,
10182 parentDelTrigger
, parentUpdTrigger
,
10183 &deleteTriggerOid
, &updateTriggerOid
);
10186 * If the referenced table is partitioned, recurse on ourselves to handle
10187 * each partition. We need one pg_constraint row created for each
10188 * partition in addition to the pg_constraint row for the parent table.
10190 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10192 PartitionDesc pd
= RelationGetPartitionDesc(pkrel
, true);
10194 for (int i
= 0; i
< pd
->nparts
; i
++)
10198 AttrNumber
*mapped_pkattnum
;
10201 partRel
= table_open(pd
->oids
[i
], ShareRowExclusiveLock
);
10204 * Map the attribute numbers in the referenced side of the FK
10205 * definition to match the partition's column layout.
10207 map
= build_attrmap_by_name_if_req(RelationGetDescr(partRel
),
10208 RelationGetDescr(pkrel
),
10212 mapped_pkattnum
= palloc(sizeof(AttrNumber
) * numfks
);
10213 for (int j
= 0; j
< numfks
; j
++)
10214 mapped_pkattnum
[j
] = map
->attnums
[pkattnum
[j
] - 1];
10217 mapped_pkattnum
= pkattnum
;
10220 partIndexId
= index_get_partition(partRel
, indexOid
);
10221 if (!OidIsValid(partIndexId
))
10222 elog(ERROR
, "index for %u not found in partition %s",
10223 indexOid
, RelationGetRelationName(partRel
));
10224 addFkRecurseReferenced(wqueue
, fkconstraint
, rel
, partRel
,
10225 partIndexId
, constrOid
, numfks
,
10226 mapped_pkattnum
, fkattnum
,
10227 pfeqoperators
, ppeqoperators
, ffeqoperators
,
10228 numfkdelsetcols
, fkdelsetcols
,
10230 deleteTriggerOid
, updateTriggerOid
);
10232 /* Done -- clean up (but keep the lock) */
10233 table_close(partRel
, NoLock
);
10236 pfree(mapped_pkattnum
);
10246 * addFkRecurseReferencing
10247 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10249 * If the referencing relation is a plain relation, create the necessary check
10250 * triggers that implement the constraint, and set up for Phase 3 constraint
10251 * verification. If the referencing relation is a partitioned table, then
10252 * we create a pg_constraint row for it and recurse on this routine for each
10255 * We assume that the referenced relation is locked against concurrent
10256 * deletions. If it's a partitioned relation, every partition must be so
10259 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10260 * of an ALTER TABLE sequence.
10261 * fkconstraint is the constraint being added.
10262 * rel is the referencing relation; might be a partition, if recursing.
10263 * pkrel is the root referenced relation.
10264 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10265 * parentConstr is the OID of the parent constraint (there is always one).
10266 * numfks is the number of columns in the foreign key
10267 * pkattnum is the attnum array of referenced attributes.
10268 * fkattnum is the attnum array of referencing attributes.
10269 * pf/pp/ffeqoperators are OID array of operators between columns.
10270 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10272 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10273 * NULL/DEFAULT clause
10274 * old_check_ok signals that this constraint replaces an existing one that
10275 * was already validated (thus this one doesn't need validation).
10276 * lockmode is the lockmode to acquire on partitions when recursing.
10277 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10278 * a partition, are the OIDs of the parent check triggers for INSERT and
10279 * UPDATE respectively.
10282 addFkRecurseReferencing(List
**wqueue
, Constraint
*fkconstraint
, Relation rel
,
10283 Relation pkrel
, Oid indexOid
, Oid parentConstr
,
10284 int numfks
, int16
*pkattnum
, int16
*fkattnum
,
10285 Oid
*pfeqoperators
, Oid
*ppeqoperators
, Oid
*ffeqoperators
,
10286 int numfkdelsetcols
, int16
*fkdelsetcols
,
10287 bool old_check_ok
, LOCKMODE lockmode
,
10288 Oid parentInsTrigger
, Oid parentUpdTrigger
)
10290 Oid insertTriggerOid
,
10293 Assert(OidIsValid(parentConstr
));
10295 if (rel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10297 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10298 errmsg("foreign key constraints are not supported on foreign tables")));
10301 * Add the check triggers to it and, if necessary, schedule it to be
10302 * checked in Phase 3.
10304 * If the relation is partitioned, drill down to do it to its partitions.
10306 createForeignKeyCheckTriggers(RelationGetRelid(rel
),
10307 RelationGetRelid(pkrel
),
10311 parentInsTrigger
, parentUpdTrigger
,
10312 &insertTriggerOid
, &updateTriggerOid
);
10314 if (rel
->rd_rel
->relkind
== RELKIND_RELATION
)
10317 * Tell Phase 3 to check that the constraint is satisfied by existing
10318 * rows. We can skip this during table creation, when requested
10319 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10320 * and when we're recreating a constraint following a SET DATA TYPE
10321 * operation that did not impugn its validity.
10323 if (wqueue
&& !old_check_ok
&& !fkconstraint
->skip_validation
)
10325 NewConstraint
*newcon
;
10326 AlteredTableInfo
*tab
;
10328 tab
= ATGetQueueEntry(wqueue
, rel
);
10330 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
10331 newcon
->name
= get_constraint_name(parentConstr
);
10332 newcon
->contype
= CONSTR_FOREIGN
;
10333 newcon
->refrelid
= RelationGetRelid(pkrel
);
10334 newcon
->refindid
= indexOid
;
10335 newcon
->conid
= parentConstr
;
10336 newcon
->qual
= (Node
*) fkconstraint
;
10338 tab
->constraints
= lappend(tab
->constraints
, newcon
);
10341 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10343 PartitionDesc pd
= RelationGetPartitionDesc(rel
, true);
10347 * Triggers of the foreign keys will be manipulated a bunch of times
10348 * in the loop below. To avoid repeatedly opening/closing the trigger
10349 * catalog relation, we open it here and pass it to the subroutines
10352 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10355 * Recurse to take appropriate action on each partition; either we
10356 * find an existing constraint to reparent to ours, or we create a new
10359 for (int i
= 0; i
< pd
->nparts
; i
++)
10361 Oid partitionId
= pd
->oids
[i
];
10362 Relation partition
= table_open(partitionId
, lockmode
);
10365 AttrNumber mapped_fkattnum
[INDEX_MAX_KEYS
];
10369 ObjectAddress address
,
10373 CheckAlterTableIsSafe(partition
);
10375 attmap
= build_attrmap_by_name(RelationGetDescr(partition
),
10376 RelationGetDescr(rel
),
10378 for (int j
= 0; j
< numfks
; j
++)
10379 mapped_fkattnum
[j
] = attmap
->attnums
[fkattnum
[j
] - 1];
10381 /* Check whether an existing constraint can be repurposed */
10382 partFKs
= copyObject(RelationGetFKeyList(partition
));
10384 foreach(cell
, partFKs
)
10386 ForeignKeyCacheInfo
*fk
;
10388 fk
= lfirst_node(ForeignKeyCacheInfo
, cell
);
10389 if (tryAttachPartitionForeignKey(fk
,
10406 table_close(partition
, NoLock
);
10411 * No luck finding a good constraint to reuse; create our own.
10413 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10414 RelationGetRelid(partition
),
10415 fkconstraint
->conname
))
10416 conname
= ChooseConstraintName(RelationGetRelationName(partition
),
10417 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10419 RelationGetNamespace(partition
), NIL
);
10421 conname
= fkconstraint
->conname
;
10423 CreateConstraintEntry(conname
,
10424 RelationGetNamespace(partition
),
10425 CONSTRAINT_FOREIGN
,
10426 fkconstraint
->deferrable
,
10427 fkconstraint
->initdeferred
,
10428 fkconstraint
->initially_valid
,
10436 RelationGetRelid(pkrel
),
10442 fkconstraint
->fk_upd_action
,
10443 fkconstraint
->fk_del_action
,
10446 fkconstraint
->fk_matchtype
,
10456 * Give this constraint partition-type dependencies on the parent
10457 * constraint as well as the table.
10459 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10460 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstr
);
10461 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10462 ObjectAddressSet(referenced
, RelationRelationId
, partitionId
);
10463 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10465 /* Make all this visible before recursing */
10466 CommandCounterIncrement();
10468 /* call ourselves to finalize the creation and we're done */
10469 addFkRecurseReferencing(wqueue
, fkconstraint
, partition
, pkrel
,
10485 table_close(partition
, NoLock
);
10488 table_close(trigrel
, RowExclusiveLock
);
10493 * CloneForeignKeyConstraints
10494 * Clone foreign keys from a partitioned table to a newly acquired
10497 * partitionRel is a partition of parentRel, so we can be certain that it has
10498 * the same columns with the same datatypes. The columns may be in different
10501 * wqueue must be passed to set up phase 3 constraint checking, unless the
10502 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10506 CloneForeignKeyConstraints(List
**wqueue
, Relation parentRel
,
10507 Relation partitionRel
)
10509 /* This only works for declarative partitioning */
10510 Assert(parentRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
10513 * Clone constraints for which the parent is on the referenced side.
10515 CloneFkReferenced(parentRel
, partitionRel
);
10518 * Now clone constraints where the parent is on the referencing side.
10520 CloneFkReferencing(wqueue
, parentRel
, partitionRel
);
10524 * CloneFkReferenced
10525 * Subroutine for CloneForeignKeyConstraints
10527 * Find all the FKs that have the parent relation on the referenced side;
10528 * clone those constraints to the given partition. This is to be called
10529 * when the partition is being created or attached.
10531 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10533 * This recurses to partitions, if the relation being attached is partitioned.
10534 * Recursion is done by calling addFkRecurseReferenced.
10537 CloneFkReferenced(Relation parentRel
, Relation partitionRel
)
10539 Relation pg_constraint
;
10543 ScanKeyData key
[2];
10549 * Search for any constraints where this partition's parent is in the
10550 * referenced side. However, we must not clone any constraint whose
10551 * parent constraint is also going to be cloned, to avoid duplicates. So
10552 * do it in two steps: first construct the list of constraints to clone,
10553 * then go over that list cloning those whose parents are not in the list.
10554 * (We must not rely on the parent being seen first, since the catalog
10555 * scan could return children first.)
10557 pg_constraint
= table_open(ConstraintRelationId
, RowShareLock
);
10558 ScanKeyInit(&key
[0],
10559 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
10560 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parentRel
)));
10561 ScanKeyInit(&key
[1],
10562 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
10563 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
10564 /* This is a seqscan, as we don't have a usable index ... */
10565 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true,
10567 while ((tuple
= systable_getnext(scan
)) != NULL
)
10569 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10571 clone
= lappend_oid(clone
, constrForm
->oid
);
10573 systable_endscan(scan
);
10574 table_close(pg_constraint
, RowShareLock
);
10577 * Triggers of the foreign keys will be manipulated a bunch of times in
10578 * the loop below. To avoid repeatedly opening/closing the trigger
10579 * catalog relation, we open it here and pass it to the subroutines called
10582 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10584 attmap
= build_attrmap_by_name(RelationGetDescr(partitionRel
),
10585 RelationGetDescr(parentRel
),
10587 foreach(cell
, clone
)
10589 Oid constrOid
= lfirst_oid(cell
);
10590 Form_pg_constraint constrForm
;
10595 AttrNumber conkey
[INDEX_MAX_KEYS
];
10596 AttrNumber mapped_confkey
[INDEX_MAX_KEYS
];
10597 AttrNumber confkey
[INDEX_MAX_KEYS
];
10598 Oid conpfeqop
[INDEX_MAX_KEYS
];
10599 Oid conppeqop
[INDEX_MAX_KEYS
];
10600 Oid conffeqop
[INDEX_MAX_KEYS
];
10601 int numfkdelsetcols
;
10602 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10603 Constraint
*fkconstraint
;
10604 Oid deleteTriggerOid
,
10607 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
10608 if (!HeapTupleIsValid(tuple
))
10609 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
10610 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10613 * As explained above: don't try to clone a constraint for which we're
10614 * going to clone the parent.
10616 if (list_member_oid(clone
, constrForm
->conparentid
))
10618 ReleaseSysCache(tuple
);
10623 * Don't clone self-referencing foreign keys, which can be in the
10624 * partitioned table or in the partition-to-be.
10626 if (constrForm
->conrelid
== RelationGetRelid(parentRel
) ||
10627 constrForm
->conrelid
== RelationGetRelid(partitionRel
))
10629 ReleaseSysCache(tuple
);
10634 * Because we're only expanding the key space at the referenced side,
10635 * we don't need to prevent any operation in the referencing table, so
10636 * AccessShareLock suffices (assumes that dropping the constraint
10639 fkRel
= table_open(constrForm
->conrelid
, AccessShareLock
);
10641 indexOid
= constrForm
->conindid
;
10642 DeconstructFkConstraintRow(tuple
,
10652 for (int i
= 0; i
< numfks
; i
++)
10653 mapped_confkey
[i
] = attmap
->attnums
[confkey
[i
] - 1];
10655 fkconstraint
= makeNode(Constraint
);
10656 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10657 fkconstraint
->conname
= NameStr(constrForm
->conname
);
10658 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10659 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10660 fkconstraint
->location
= -1;
10661 fkconstraint
->pktable
= NULL
;
10662 /* ->fk_attrs determined below */
10663 fkconstraint
->pk_attrs
= NIL
;
10664 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10665 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10666 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10667 fkconstraint
->fk_del_set_cols
= NIL
;
10668 fkconstraint
->old_conpfeqop
= NIL
;
10669 fkconstraint
->old_pktable_oid
= InvalidOid
;
10670 fkconstraint
->skip_validation
= false;
10671 fkconstraint
->initially_valid
= true;
10673 /* set up colnames that are used to generate the constraint name */
10674 for (int i
= 0; i
< numfks
; i
++)
10676 Form_pg_attribute att
;
10678 att
= TupleDescAttr(RelationGetDescr(fkRel
),
10680 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10681 makeString(NameStr(att
->attname
)));
10685 * Add the new foreign key constraint pointing to the new partition.
10686 * Because this new partition appears in the referenced side of the
10687 * constraint, we don't need to set up for Phase 3 check.
10689 partIndexId
= index_get_partition(partitionRel
, indexOid
);
10690 if (!OidIsValid(partIndexId
))
10691 elog(ERROR
, "index for %u not found in partition %s",
10692 indexOid
, RelationGetRelationName(partitionRel
));
10695 * Get the "action" triggers belonging to the constraint to pass as
10696 * parent OIDs for similar triggers that will be created on the
10697 * partition in addFkRecurseReferenced().
10699 GetForeignKeyActionTriggers(trigrel
, constrOid
,
10700 constrForm
->confrelid
, constrForm
->conrelid
,
10701 &deleteTriggerOid
, &updateTriggerOid
);
10703 addFkRecurseReferenced(NULL
,
10721 table_close(fkRel
, NoLock
);
10722 ReleaseSysCache(tuple
);
10725 table_close(trigrel
, RowExclusiveLock
);
10729 * CloneFkReferencing
10730 * Subroutine for CloneForeignKeyConstraints
10732 * For each FK constraint of the parent relation in the given list, find an
10733 * equivalent constraint in its partition relation that can be reparented;
10734 * if one cannot be found, create a new constraint in the partition as its
10737 * If wqueue is given, it is used to set up phase-3 verification for each
10738 * cloned constraint; if omitted, we assume that such verification is not
10739 * needed (example: the partition is being created anew).
10742 CloneFkReferencing(List
**wqueue
, Relation parentRel
, Relation partRel
)
10750 /* obtain a list of constraints that we need to clone */
10751 foreach(cell
, RelationGetFKeyList(parentRel
))
10753 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
10755 clone
= lappend_oid(clone
, fk
->conoid
);
10759 * Silently do nothing if there's nothing to do. In particular, this
10760 * avoids throwing a spurious error for foreign tables.
10765 if (partRel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
10767 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
10768 errmsg("foreign key constraints are not supported on foreign tables")));
10771 * Triggers of the foreign keys will be manipulated a bunch of times in
10772 * the loop below. To avoid repeatedly opening/closing the trigger
10773 * catalog relation, we open it here and pass it to the subroutines called
10776 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
10779 * The constraint key may differ, if the columns in the partition are
10780 * different. This map is used to convert them.
10782 attmap
= build_attrmap_by_name(RelationGetDescr(partRel
),
10783 RelationGetDescr(parentRel
),
10786 partFKs
= copyObject(RelationGetFKeyList(partRel
));
10788 foreach(cell
, clone
)
10790 Oid parentConstrOid
= lfirst_oid(cell
);
10791 Form_pg_constraint constrForm
;
10795 AttrNumber conkey
[INDEX_MAX_KEYS
];
10796 AttrNumber mapped_conkey
[INDEX_MAX_KEYS
];
10797 AttrNumber confkey
[INDEX_MAX_KEYS
];
10798 Oid conpfeqop
[INDEX_MAX_KEYS
];
10799 Oid conppeqop
[INDEX_MAX_KEYS
];
10800 Oid conffeqop
[INDEX_MAX_KEYS
];
10801 int numfkdelsetcols
;
10802 AttrNumber confdelsetcols
[INDEX_MAX_KEYS
];
10803 Constraint
*fkconstraint
;
10807 ObjectAddress address
,
10810 Oid insertTriggerOid
,
10813 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parentConstrOid
));
10814 if (!HeapTupleIsValid(tuple
))
10815 elog(ERROR
, "cache lookup failed for constraint %u",
10817 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
10819 /* Don't clone constraints whose parents are being cloned */
10820 if (list_member_oid(clone
, constrForm
->conparentid
))
10822 ReleaseSysCache(tuple
);
10827 * Need to prevent concurrent deletions. If pkrel is a partitioned
10828 * relation, that means to lock all partitions.
10830 pkrel
= table_open(constrForm
->confrelid
, ShareRowExclusiveLock
);
10831 if (pkrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
10832 (void) find_all_inheritors(RelationGetRelid(pkrel
),
10833 ShareRowExclusiveLock
, NULL
);
10835 DeconstructFkConstraintRow(tuple
, &numfks
, conkey
, confkey
,
10836 conpfeqop
, conppeqop
, conffeqop
,
10837 &numfkdelsetcols
, confdelsetcols
);
10838 for (int i
= 0; i
< numfks
; i
++)
10839 mapped_conkey
[i
] = attmap
->attnums
[conkey
[i
] - 1];
10842 * Get the "check" triggers belonging to the constraint to pass as
10843 * parent OIDs for similar triggers that will be created on the
10844 * partition in addFkRecurseReferencing(). They are also passed to
10845 * tryAttachPartitionForeignKey() below to simply assign as parents to
10846 * the partition's existing "check" triggers, that is, if the
10847 * corresponding constraints is deemed attachable to the parent
10850 GetForeignKeyCheckTriggers(trigrel
, constrForm
->oid
,
10851 constrForm
->confrelid
, constrForm
->conrelid
,
10852 &insertTriggerOid
, &updateTriggerOid
);
10855 * Before creating a new constraint, see whether any existing FKs are
10856 * fit for the purpose. If one is, attach the parent constraint to
10857 * it, and don't clone anything. This way we avoid the expensive
10858 * verification step and don't end up with a duplicate FK, and we
10859 * don't need to recurse to partitions for this constraint.
10862 foreach(lc
, partFKs
)
10864 ForeignKeyCacheInfo
*fk
= lfirst_node(ForeignKeyCacheInfo
, lc
);
10866 if (tryAttachPartitionForeignKey(fk
,
10867 RelationGetRelid(partRel
),
10878 table_close(pkrel
, NoLock
);
10884 ReleaseSysCache(tuple
);
10888 /* No dice. Set up to create our own constraint */
10889 fkconstraint
= makeNode(Constraint
);
10890 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
10891 /* ->conname determined below */
10892 fkconstraint
->deferrable
= constrForm
->condeferrable
;
10893 fkconstraint
->initdeferred
= constrForm
->condeferred
;
10894 fkconstraint
->location
= -1;
10895 fkconstraint
->pktable
= NULL
;
10896 /* ->fk_attrs determined below */
10897 fkconstraint
->pk_attrs
= NIL
;
10898 fkconstraint
->fk_matchtype
= constrForm
->confmatchtype
;
10899 fkconstraint
->fk_upd_action
= constrForm
->confupdtype
;
10900 fkconstraint
->fk_del_action
= constrForm
->confdeltype
;
10901 fkconstraint
->fk_del_set_cols
= NIL
;
10902 fkconstraint
->old_conpfeqop
= NIL
;
10903 fkconstraint
->old_pktable_oid
= InvalidOid
;
10904 fkconstraint
->skip_validation
= false;
10905 fkconstraint
->initially_valid
= true;
10906 for (int i
= 0; i
< numfks
; i
++)
10908 Form_pg_attribute att
;
10910 att
= TupleDescAttr(RelationGetDescr(partRel
),
10911 mapped_conkey
[i
] - 1);
10912 fkconstraint
->fk_attrs
= lappend(fkconstraint
->fk_attrs
,
10913 makeString(NameStr(att
->attname
)));
10915 if (ConstraintNameIsUsed(CONSTRAINT_RELATION
,
10916 RelationGetRelid(partRel
),
10917 NameStr(constrForm
->conname
)))
10918 fkconstraint
->conname
=
10919 ChooseConstraintName(RelationGetRelationName(partRel
),
10920 ChooseForeignKeyConstraintNameAddition(fkconstraint
->fk_attrs
),
10922 RelationGetNamespace(partRel
), NIL
);
10924 fkconstraint
->conname
= pstrdup(NameStr(constrForm
->conname
));
10926 indexOid
= constrForm
->conindid
;
10928 CreateConstraintEntry(fkconstraint
->conname
,
10929 constrForm
->connamespace
,
10930 CONSTRAINT_FOREIGN
,
10931 fkconstraint
->deferrable
,
10932 fkconstraint
->initdeferred
,
10933 constrForm
->convalidated
,
10935 RelationGetRelid(partRel
),
10939 InvalidOid
, /* not a domain constraint */
10941 constrForm
->confrelid
, /* same foreign rel */
10947 fkconstraint
->fk_upd_action
,
10948 fkconstraint
->fk_del_action
,
10951 fkconstraint
->fk_matchtype
,
10955 false, /* islocal */
10957 false, /* conNoInherit */
10960 /* Set up partition dependencies for the new constraint */
10961 ObjectAddressSet(address
, ConstraintRelationId
, constrOid
);
10962 ObjectAddressSet(referenced
, ConstraintRelationId
, parentConstrOid
);
10963 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_PRI
);
10964 ObjectAddressSet(referenced
, RelationRelationId
,
10965 RelationGetRelid(partRel
));
10966 recordDependencyOn(&address
, &referenced
, DEPENDENCY_PARTITION_SEC
);
10968 /* Done with the cloned constraint's tuple */
10969 ReleaseSysCache(tuple
);
10971 /* Make all this visible before recursing */
10972 CommandCounterIncrement();
10974 addFkRecurseReferencing(wqueue
,
10988 false, /* no old check exists */
10989 AccessExclusiveLock
,
10992 table_close(pkrel
, NoLock
);
10995 table_close(trigrel
, RowExclusiveLock
);
10999 * When the parent of a partition receives [the referencing side of] a foreign
11000 * key, we must propagate that foreign key to the partition. However, the
11001 * partition might already have an equivalent foreign key; this routine
11002 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11003 * by the other parameters. If they are equivalent, create the link between
11004 * the two constraints and return true.
11006 * If the given FK does not match the one defined by rest of the params,
11010 tryAttachPartitionForeignKey(ForeignKeyCacheInfo
*fk
,
11012 Oid parentConstrOid
,
11014 AttrNumber
*mapped_conkey
,
11015 AttrNumber
*confkey
,
11017 Oid parentInsTrigger
,
11018 Oid parentUpdTrigger
,
11021 HeapTuple parentConstrTup
;
11022 Form_pg_constraint parentConstr
;
11023 HeapTuple partcontup
;
11024 Form_pg_constraint partConstr
;
11028 Oid insertTriggerOid
,
11031 parentConstrTup
= SearchSysCache1(CONSTROID
,
11032 ObjectIdGetDatum(parentConstrOid
));
11033 if (!HeapTupleIsValid(parentConstrTup
))
11034 elog(ERROR
, "cache lookup failed for constraint %u", parentConstrOid
);
11035 parentConstr
= (Form_pg_constraint
) GETSTRUCT(parentConstrTup
);
11038 * Do some quick & easy initial checks. If any of these fail, we cannot
11039 * use this constraint.
11041 if (fk
->confrelid
!= parentConstr
->confrelid
|| fk
->nkeys
!= numfks
)
11043 ReleaseSysCache(parentConstrTup
);
11046 for (int i
= 0; i
< numfks
; i
++)
11048 if (fk
->conkey
[i
] != mapped_conkey
[i
] ||
11049 fk
->confkey
[i
] != confkey
[i
] ||
11050 fk
->conpfeqop
[i
] != conpfeqop
[i
])
11052 ReleaseSysCache(parentConstrTup
);
11058 * Looks good so far; do some more extensive checks. Presumably the check
11059 * for 'convalidated' could be dropped, since we don't really care about
11060 * that, but let's be careful for now.
11062 partcontup
= SearchSysCache1(CONSTROID
,
11063 ObjectIdGetDatum(fk
->conoid
));
11064 if (!HeapTupleIsValid(partcontup
))
11065 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
11066 partConstr
= (Form_pg_constraint
) GETSTRUCT(partcontup
);
11067 if (OidIsValid(partConstr
->conparentid
) ||
11068 !partConstr
->convalidated
||
11069 partConstr
->condeferrable
!= parentConstr
->condeferrable
||
11070 partConstr
->condeferred
!= parentConstr
->condeferred
||
11071 partConstr
->confupdtype
!= parentConstr
->confupdtype
||
11072 partConstr
->confdeltype
!= parentConstr
->confdeltype
||
11073 partConstr
->confmatchtype
!= parentConstr
->confmatchtype
)
11075 ReleaseSysCache(parentConstrTup
);
11076 ReleaseSysCache(partcontup
);
11080 ReleaseSysCache(partcontup
);
11081 ReleaseSysCache(parentConstrTup
);
11084 * Looks good! Attach this constraint. The action triggers in the new
11085 * partition become redundant -- the parent table already has equivalent
11086 * ones, and those will be able to reach the partition. Remove the ones
11087 * in the partition. We identify them because they have our constraint
11088 * OID, as well as being on the referenced rel.
11091 Anum_pg_trigger_tgconstraint
,
11092 BTEqualStrategyNumber
, F_OIDEQ
,
11093 ObjectIdGetDatum(fk
->conoid
));
11094 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11096 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11098 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11099 ObjectAddress trigger
;
11101 if (trgform
->tgconstrrelid
!= fk
->conrelid
)
11103 if (trgform
->tgrelid
!= fk
->confrelid
)
11107 * The constraint is originally set up to contain this trigger as an
11108 * implementation object, so there's a dependency record that links
11109 * the two; however, since the trigger is no longer needed, we remove
11110 * the dependency link in order to be able to drop the trigger while
11111 * keeping the constraint intact.
11113 deleteDependencyRecordsFor(TriggerRelationId
,
11116 /* make dependency deletion visible to performDeletion */
11117 CommandCounterIncrement();
11118 ObjectAddressSet(trigger
, TriggerRelationId
,
11120 performDeletion(&trigger
, DROP_RESTRICT
, 0);
11121 /* make trigger drop visible, in case the loop iterates */
11122 CommandCounterIncrement();
11125 systable_endscan(scan
);
11127 ConstraintSetParentConstraint(fk
->conoid
, parentConstrOid
, partRelid
);
11130 * Like the constraint, attach partition's "check" triggers to the
11131 * corresponding parent triggers.
11133 GetForeignKeyCheckTriggers(trigrel
,
11134 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
11135 &insertTriggerOid
, &updateTriggerOid
);
11136 Assert(OidIsValid(insertTriggerOid
) && OidIsValid(parentInsTrigger
));
11137 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, parentInsTrigger
,
11139 Assert(OidIsValid(updateTriggerOid
) && OidIsValid(parentUpdTrigger
));
11140 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, parentUpdTrigger
,
11143 CommandCounterIncrement();
11148 * GetForeignKeyActionTriggers
11149 * Returns delete and update "action" triggers of the given relation
11150 * belonging to the given constraint
11153 GetForeignKeyActionTriggers(Relation trigrel
,
11154 Oid conoid
, Oid confrelid
, Oid conrelid
,
11155 Oid
*deleteTriggerOid
,
11156 Oid
*updateTriggerOid
)
11162 *deleteTriggerOid
= *updateTriggerOid
= InvalidOid
;
11164 Anum_pg_trigger_tgconstraint
,
11165 BTEqualStrategyNumber
, F_OIDEQ
,
11166 ObjectIdGetDatum(conoid
));
11168 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11170 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11172 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11174 if (trgform
->tgconstrrelid
!= conrelid
)
11176 if (trgform
->tgrelid
!= confrelid
)
11178 /* Only ever look at "action" triggers on the PK side. */
11179 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_PK
)
11181 if (TRIGGER_FOR_DELETE(trgform
->tgtype
))
11183 Assert(*deleteTriggerOid
== InvalidOid
);
11184 *deleteTriggerOid
= trgform
->oid
;
11186 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11188 Assert(*updateTriggerOid
== InvalidOid
);
11189 *updateTriggerOid
= trgform
->oid
;
11191 #ifndef USE_ASSERT_CHECKING
11192 /* In an assert-enabled build, continue looking to find duplicates */
11193 if (OidIsValid(*deleteTriggerOid
) && OidIsValid(*updateTriggerOid
))
11198 if (!OidIsValid(*deleteTriggerOid
))
11199 elog(ERROR
, "could not find ON DELETE action trigger of foreign key constraint %u",
11201 if (!OidIsValid(*updateTriggerOid
))
11202 elog(ERROR
, "could not find ON UPDATE action trigger of foreign key constraint %u",
11205 systable_endscan(scan
);
11209 * GetForeignKeyCheckTriggers
11210 * Returns insert and update "check" triggers of the given relation
11211 * belonging to the given constraint
11214 GetForeignKeyCheckTriggers(Relation trigrel
,
11215 Oid conoid
, Oid confrelid
, Oid conrelid
,
11216 Oid
*insertTriggerOid
,
11217 Oid
*updateTriggerOid
)
11223 *insertTriggerOid
= *updateTriggerOid
= InvalidOid
;
11225 Anum_pg_trigger_tgconstraint
,
11226 BTEqualStrategyNumber
, F_OIDEQ
,
11227 ObjectIdGetDatum(conoid
));
11229 scan
= systable_beginscan(trigrel
, TriggerConstraintIndexId
, true,
11231 while ((trigtup
= systable_getnext(scan
)) != NULL
)
11233 Form_pg_trigger trgform
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
11235 if (trgform
->tgconstrrelid
!= confrelid
)
11237 if (trgform
->tgrelid
!= conrelid
)
11239 /* Only ever look at "check" triggers on the FK side. */
11240 if (RI_FKey_trigger_type(trgform
->tgfoid
) != RI_TRIGGER_FK
)
11242 if (TRIGGER_FOR_INSERT(trgform
->tgtype
))
11244 Assert(*insertTriggerOid
== InvalidOid
);
11245 *insertTriggerOid
= trgform
->oid
;
11247 else if (TRIGGER_FOR_UPDATE(trgform
->tgtype
))
11249 Assert(*updateTriggerOid
== InvalidOid
);
11250 *updateTriggerOid
= trgform
->oid
;
11252 #ifndef USE_ASSERT_CHECKING
11253 /* In an assert-enabled build, continue looking to find duplicates. */
11254 if (OidIsValid(*insertTriggerOid
) && OidIsValid(*updateTriggerOid
))
11259 if (!OidIsValid(*insertTriggerOid
))
11260 elog(ERROR
, "could not find ON INSERT check triggers of foreign key constraint %u",
11262 if (!OidIsValid(*updateTriggerOid
))
11263 elog(ERROR
, "could not find ON UPDATE check triggers of foreign key constraint %u",
11266 systable_endscan(scan
);
11270 * ALTER TABLE ALTER CONSTRAINT
11272 * Update the attributes of a constraint.
11274 * Currently only works for Foreign Key constraints.
11276 * If the constraint is modified, returns its address; otherwise, return
11277 * InvalidObjectAddress.
11279 static ObjectAddress
11280 ATExecAlterConstraint(Relation rel
, AlterTableCmd
*cmd
, bool recurse
,
11281 bool recursing
, LOCKMODE lockmode
)
11283 Constraint
*cmdcon
;
11287 ScanKeyData skey
[3];
11288 HeapTuple contuple
;
11289 Form_pg_constraint currcon
;
11290 ObjectAddress address
;
11291 List
*otherrelids
= NIL
;
11294 cmdcon
= castNode(Constraint
, cmd
->def
);
11296 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11297 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
11300 * Find and check the target constraint
11302 ScanKeyInit(&skey
[0],
11303 Anum_pg_constraint_conrelid
,
11304 BTEqualStrategyNumber
, F_OIDEQ
,
11305 ObjectIdGetDatum(RelationGetRelid(rel
)));
11306 ScanKeyInit(&skey
[1],
11307 Anum_pg_constraint_contypid
,
11308 BTEqualStrategyNumber
, F_OIDEQ
,
11309 ObjectIdGetDatum(InvalidOid
));
11310 ScanKeyInit(&skey
[2],
11311 Anum_pg_constraint_conname
,
11312 BTEqualStrategyNumber
, F_NAMEEQ
,
11313 CStringGetDatum(cmdcon
->conname
));
11314 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11315 true, NULL
, 3, skey
);
11317 /* There can be at most one matching row */
11318 if (!HeapTupleIsValid(contuple
= systable_getnext(scan
)))
11320 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11321 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11322 cmdcon
->conname
, RelationGetRelationName(rel
))));
11324 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11325 if (currcon
->contype
!= CONSTRAINT_FOREIGN
)
11327 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11328 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11329 cmdcon
->conname
, RelationGetRelationName(rel
))));
11332 * If it's not the topmost constraint, raise an error.
11334 * Altering a non-topmost constraint leaves some triggers untouched, since
11335 * they are not directly connected to this constraint; also, pg_dump would
11336 * ignore the deferrability status of the individual constraint, since it
11337 * only dumps topmost constraints. Avoid these problems by refusing this
11338 * operation and telling the user to alter the parent constraint instead.
11340 if (OidIsValid(currcon
->conparentid
))
11343 Oid parent
= currcon
->conparentid
;
11344 char *ancestorname
= NULL
;
11345 char *ancestortable
= NULL
;
11347 /* Loop to find the topmost constraint */
11348 while (HeapTupleIsValid(tp
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(parent
))))
11350 Form_pg_constraint contup
= (Form_pg_constraint
) GETSTRUCT(tp
);
11352 /* If no parent, this is the constraint we want */
11353 if (!OidIsValid(contup
->conparentid
))
11355 ancestorname
= pstrdup(NameStr(contup
->conname
));
11356 ancestortable
= get_rel_name(contup
->conrelid
);
11357 ReleaseSysCache(tp
);
11361 parent
= contup
->conparentid
;
11362 ReleaseSysCache(tp
);
11366 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11367 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11368 cmdcon
->conname
, RelationGetRelationName(rel
)),
11369 ancestorname
&& ancestortable
?
11370 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11371 cmdcon
->conname
, ancestorname
, ancestortable
) : 0,
11372 errhint("You may alter the constraint it derives from instead.")));
11376 * Do the actual catalog work. We can skip changing if already in the
11377 * desired state, but not if a partitioned table: partitions need to be
11378 * processed regardless, in case they had the constraint locally changed.
11380 address
= InvalidObjectAddress
;
11381 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11382 currcon
->condeferred
!= cmdcon
->initdeferred
||
11383 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
11385 if (ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, rel
, contuple
,
11386 &otherrelids
, lockmode
))
11387 ObjectAddressSet(address
, ConstraintRelationId
, currcon
->oid
);
11391 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11392 * having the constraint itself; here we also invalidate for relations
11393 * that have any triggers that are part of the constraint.
11395 foreach(lc
, otherrelids
)
11396 CacheInvalidateRelcacheByRelid(lfirst_oid(lc
));
11398 systable_endscan(scan
);
11400 table_close(tgrel
, RowExclusiveLock
);
11401 table_close(conrel
, RowExclusiveLock
);
11407 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11408 * constraint is altered.
11410 * *otherrelids is appended OIDs of relations containing affected triggers.
11412 * Note that we must recurse even when the values are correct, in case
11413 * indirect descendants have had their constraints altered locally.
11414 * (This could be avoided if we forbade altering constraints in partitions
11415 * but existing releases don't do that.)
11418 ATExecAlterConstrRecurse(Constraint
*cmdcon
, Relation conrel
, Relation tgrel
,
11419 Relation rel
, HeapTuple contuple
, List
**otherrelids
,
11422 Form_pg_constraint currcon
;
11425 bool changed
= false;
11427 /* since this function recurses, it could be driven to stack overflow */
11428 check_stack_depth();
11430 currcon
= (Form_pg_constraint
) GETSTRUCT(contuple
);
11431 conoid
= currcon
->oid
;
11432 refrelid
= currcon
->confrelid
;
11435 * Update pg_constraint with the flags from cmdcon.
11437 * If called to modify a constraint that's already in the desired state,
11438 * silently do nothing.
11440 if (currcon
->condeferrable
!= cmdcon
->deferrable
||
11441 currcon
->condeferred
!= cmdcon
->initdeferred
)
11443 HeapTuple copyTuple
;
11444 Form_pg_constraint copy_con
;
11447 SysScanDesc tgscan
;
11449 copyTuple
= heap_copytuple(contuple
);
11450 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11451 copy_con
->condeferrable
= cmdcon
->deferrable
;
11452 copy_con
->condeferred
= cmdcon
->initdeferred
;
11453 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11455 InvokeObjectPostAlterHook(ConstraintRelationId
,
11458 heap_freetuple(copyTuple
);
11461 /* Make new constraint flags visible to others */
11462 CacheInvalidateRelcache(rel
);
11465 * Now we need to update the multiple entries in pg_trigger that
11466 * implement the constraint.
11468 ScanKeyInit(&tgkey
,
11469 Anum_pg_trigger_tgconstraint
,
11470 BTEqualStrategyNumber
, F_OIDEQ
,
11471 ObjectIdGetDatum(conoid
));
11472 tgscan
= systable_beginscan(tgrel
, TriggerConstraintIndexId
, true,
11474 while (HeapTupleIsValid(tgtuple
= systable_getnext(tgscan
)))
11476 Form_pg_trigger tgform
= (Form_pg_trigger
) GETSTRUCT(tgtuple
);
11477 Form_pg_trigger copy_tg
;
11478 HeapTuple tgCopyTuple
;
11481 * Remember OIDs of other relation(s) involved in FK constraint.
11482 * (Note: it's likely that we could skip forcing a relcache inval
11483 * for other rels that don't have a trigger whose properties
11484 * change, but let's be conservative.)
11486 if (tgform
->tgrelid
!= RelationGetRelid(rel
))
11487 *otherrelids
= list_append_unique_oid(*otherrelids
,
11491 * Update deferrability of RI_FKey_noaction_del,
11492 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11493 * triggers, but not others; see createForeignKeyActionTriggers
11494 * and CreateFKCheckTrigger.
11496 if (tgform
->tgfoid
!= F_RI_FKEY_NOACTION_DEL
&&
11497 tgform
->tgfoid
!= F_RI_FKEY_NOACTION_UPD
&&
11498 tgform
->tgfoid
!= F_RI_FKEY_CHECK_INS
&&
11499 tgform
->tgfoid
!= F_RI_FKEY_CHECK_UPD
)
11502 tgCopyTuple
= heap_copytuple(tgtuple
);
11503 copy_tg
= (Form_pg_trigger
) GETSTRUCT(tgCopyTuple
);
11505 copy_tg
->tgdeferrable
= cmdcon
->deferrable
;
11506 copy_tg
->tginitdeferred
= cmdcon
->initdeferred
;
11507 CatalogTupleUpdate(tgrel
, &tgCopyTuple
->t_self
, tgCopyTuple
);
11509 InvokeObjectPostAlterHook(TriggerRelationId
, tgform
->oid
, 0);
11511 heap_freetuple(tgCopyTuple
);
11514 systable_endscan(tgscan
);
11518 * If the table at either end of the constraint is partitioned, we need to
11519 * recurse and handle every constraint that is a child of this one.
11521 * (This assumes that the recurse flag is forcibly set for partitioned
11522 * tables, and not set for legacy inheritance, though we don't check for
11525 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
||
11526 get_rel_relkind(refrelid
) == RELKIND_PARTITIONED_TABLE
)
11530 HeapTuple childtup
;
11533 Anum_pg_constraint_conparentid
,
11534 BTEqualStrategyNumber
, F_OIDEQ
,
11535 ObjectIdGetDatum(conoid
));
11537 pscan
= systable_beginscan(conrel
, ConstraintParentIndexId
,
11538 true, NULL
, 1, &pkey
);
11540 while (HeapTupleIsValid(childtup
= systable_getnext(pscan
)))
11542 Form_pg_constraint childcon
= (Form_pg_constraint
) GETSTRUCT(childtup
);
11545 childrel
= table_open(childcon
->conrelid
, lockmode
);
11546 ATExecAlterConstrRecurse(cmdcon
, conrel
, tgrel
, childrel
, childtup
,
11547 otherrelids
, lockmode
);
11548 table_close(childrel
, NoLock
);
11551 systable_endscan(pscan
);
11558 * ALTER TABLE VALIDATE CONSTRAINT
11560 * XXX The reason we handle recursion here rather than at Phase 1 is because
11561 * there's no good way to skip recursing when handling foreign keys: there is
11562 * no need to lock children in that case, yet we wouldn't be able to avoid
11563 * doing so at that level.
11565 * Return value is the address of the validated constraint. If the constraint
11566 * was already validated, InvalidObjectAddress is returned.
11568 static ObjectAddress
11569 ATExecValidateConstraint(List
**wqueue
, Relation rel
, char *constrName
,
11570 bool recurse
, bool recursing
, LOCKMODE lockmode
)
11574 ScanKeyData skey
[3];
11576 Form_pg_constraint con
;
11577 ObjectAddress address
;
11579 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
11582 * Find and check the target constraint
11584 ScanKeyInit(&skey
[0],
11585 Anum_pg_constraint_conrelid
,
11586 BTEqualStrategyNumber
, F_OIDEQ
,
11587 ObjectIdGetDatum(RelationGetRelid(rel
)));
11588 ScanKeyInit(&skey
[1],
11589 Anum_pg_constraint_contypid
,
11590 BTEqualStrategyNumber
, F_OIDEQ
,
11591 ObjectIdGetDatum(InvalidOid
));
11592 ScanKeyInit(&skey
[2],
11593 Anum_pg_constraint_conname
,
11594 BTEqualStrategyNumber
, F_NAMEEQ
,
11595 CStringGetDatum(constrName
));
11596 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
11597 true, NULL
, 3, skey
);
11599 /* There can be at most one matching row */
11600 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
11602 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11603 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11604 constrName
, RelationGetRelationName(rel
))));
11606 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
11607 if (con
->contype
!= CONSTRAINT_FOREIGN
&&
11608 con
->contype
!= CONSTRAINT_CHECK
)
11610 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
11611 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11612 constrName
, RelationGetRelationName(rel
))));
11614 if (!con
->convalidated
)
11616 AlteredTableInfo
*tab
;
11617 HeapTuple copyTuple
;
11618 Form_pg_constraint copy_con
;
11620 if (con
->contype
== CONSTRAINT_FOREIGN
)
11622 NewConstraint
*newcon
;
11623 Constraint
*fkconstraint
;
11625 /* Queue validation for phase 3 */
11626 fkconstraint
= makeNode(Constraint
);
11627 /* for now this is all we need */
11628 fkconstraint
->conname
= constrName
;
11630 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11631 newcon
->name
= constrName
;
11632 newcon
->contype
= CONSTR_FOREIGN
;
11633 newcon
->refrelid
= con
->confrelid
;
11634 newcon
->refindid
= con
->conindid
;
11635 newcon
->conid
= con
->oid
;
11636 newcon
->qual
= (Node
*) fkconstraint
;
11638 /* Find or create work queue entry for this table */
11639 tab
= ATGetQueueEntry(wqueue
, rel
);
11640 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11643 * We disallow creating invalid foreign keys to or from
11644 * partitioned tables, so ignoring the recursion bit is okay.
11647 else if (con
->contype
== CONSTRAINT_CHECK
)
11649 List
*children
= NIL
;
11651 NewConstraint
*newcon
;
11656 * If we're recursing, the parent has already done this, so skip
11657 * it. Also, if the constraint is a NO INHERIT constraint, we
11658 * shouldn't try to look for it in the children.
11660 if (!recursing
&& !con
->connoinherit
)
11661 children
= find_all_inheritors(RelationGetRelid(rel
),
11665 * For CHECK constraints, we must ensure that we only mark the
11666 * constraint as validated on the parent if it's already validated
11669 * We recurse before validating on the parent, to reduce risk of
11672 foreach(child
, children
)
11674 Oid childoid
= lfirst_oid(child
);
11677 if (childoid
== RelationGetRelid(rel
))
11681 * If we are told not to recurse, there had better not be any
11682 * child tables, because we can't mark the constraint on the
11683 * parent valid unless it is valid for all child tables.
11687 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
11688 errmsg("constraint must be validated on child tables too")));
11690 /* find_all_inheritors already got lock */
11691 childrel
= table_open(childoid
, NoLock
);
11693 ATExecValidateConstraint(wqueue
, childrel
, constrName
, false,
11695 table_close(childrel
, NoLock
);
11698 /* Queue validation for phase 3 */
11699 newcon
= (NewConstraint
*) palloc0(sizeof(NewConstraint
));
11700 newcon
->name
= constrName
;
11701 newcon
->contype
= CONSTR_CHECK
;
11702 newcon
->refrelid
= InvalidOid
;
11703 newcon
->refindid
= InvalidOid
;
11704 newcon
->conid
= con
->oid
;
11706 val
= SysCacheGetAttrNotNull(CONSTROID
, tuple
,
11707 Anum_pg_constraint_conbin
);
11708 conbin
= TextDatumGetCString(val
);
11709 newcon
->qual
= (Node
*) stringToNode(conbin
);
11711 /* Find or create work queue entry for this table */
11712 tab
= ATGetQueueEntry(wqueue
, rel
);
11713 tab
->constraints
= lappend(tab
->constraints
, newcon
);
11716 * Invalidate relcache so that others see the new validated
11719 CacheInvalidateRelcache(rel
);
11723 * Now update the catalog, while we have the door open.
11725 copyTuple
= heap_copytuple(tuple
);
11726 copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
11727 copy_con
->convalidated
= true;
11728 CatalogTupleUpdate(conrel
, ©Tuple
->t_self
, copyTuple
);
11730 InvokeObjectPostAlterHook(ConstraintRelationId
, con
->oid
, 0);
11732 heap_freetuple(copyTuple
);
11734 ObjectAddressSet(address
, ConstraintRelationId
, con
->oid
);
11737 address
= InvalidObjectAddress
; /* already validated */
11739 systable_endscan(scan
);
11741 table_close(conrel
, RowExclusiveLock
);
11748 * transformColumnNameList - transform list of column names
11750 * Lookup each name and return its attnum and, optionally, type OID
11752 * Note: the name of this function suggests that it's general-purpose,
11753 * but actually it's only used to look up names appearing in foreign-key
11754 * clauses. The error messages would need work to use it in other cases,
11755 * and perhaps the validity checks as well.
11758 transformColumnNameList(Oid relId
, List
*colList
,
11759 int16
*attnums
, Oid
*atttypids
)
11765 foreach(l
, colList
)
11767 char *attname
= strVal(lfirst(l
));
11768 HeapTuple atttuple
;
11769 Form_pg_attribute attform
;
11771 atttuple
= SearchSysCacheAttName(relId
, attname
);
11772 if (!HeapTupleIsValid(atttuple
))
11774 (errcode(ERRCODE_UNDEFINED_COLUMN
),
11775 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11777 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
11778 if (attform
->attnum
< 0)
11780 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
11781 errmsg("system columns cannot be used in foreign keys")));
11782 if (attnum
>= INDEX_MAX_KEYS
)
11784 (errcode(ERRCODE_TOO_MANY_COLUMNS
),
11785 errmsg("cannot have more than %d keys in a foreign key",
11787 attnums
[attnum
] = attform
->attnum
;
11788 if (atttypids
!= NULL
)
11789 atttypids
[attnum
] = attform
->atttypid
;
11790 ReleaseSysCache(atttuple
);
11798 * transformFkeyGetPrimaryKey -
11800 * Look up the names, attnums, and types of the primary key attributes
11801 * for the pkrel. Also return the index OID and index opclasses of the
11802 * index supporting the primary key.
11804 * All parameters except pkrel are output parameters. Also, the function
11805 * return value is the number of attributes in the primary key.
11807 * Used when the column list in the REFERENCES specification is omitted.
11810 transformFkeyGetPrimaryKey(Relation pkrel
, Oid
*indexOid
,
11811 List
**attnamelist
,
11812 int16
*attnums
, Oid
*atttypids
,
11815 List
*indexoidlist
;
11816 ListCell
*indexoidscan
;
11817 HeapTuple indexTuple
= NULL
;
11818 Form_pg_index indexStruct
= NULL
;
11819 Datum indclassDatum
;
11820 oidvector
*indclass
;
11824 * Get the list of index OIDs for the table from the relcache, and look up
11825 * each one in the pg_index syscache until we find one marked primary key
11826 * (hopefully there isn't more than one such). Insist it's valid, too.
11828 *indexOid
= InvalidOid
;
11830 indexoidlist
= RelationGetIndexList(pkrel
);
11832 foreach(indexoidscan
, indexoidlist
)
11834 Oid indexoid
= lfirst_oid(indexoidscan
);
11836 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11837 if (!HeapTupleIsValid(indexTuple
))
11838 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11839 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11840 if (indexStruct
->indisprimary
&& indexStruct
->indisvalid
)
11843 * Refuse to use a deferrable primary key. This is per SQL spec,
11844 * and there would be a lot of interesting semantic problems if we
11845 * tried to allow it.
11847 if (!indexStruct
->indimmediate
)
11849 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
11850 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11851 RelationGetRelationName(pkrel
))));
11853 *indexOid
= indexoid
;
11856 ReleaseSysCache(indexTuple
);
11859 list_free(indexoidlist
);
11862 * Check that we found it
11864 if (!OidIsValid(*indexOid
))
11866 (errcode(ERRCODE_UNDEFINED_OBJECT
),
11867 errmsg("there is no primary key for referenced table \"%s\"",
11868 RelationGetRelationName(pkrel
))));
11870 /* Must get indclass the hard way */
11871 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11872 Anum_pg_index_indclass
);
11873 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11876 * Now build the list of PK attributes from the indkey definition (we
11877 * assume a primary key cannot have expressional elements)
11879 *attnamelist
= NIL
;
11880 for (i
= 0; i
< indexStruct
->indnkeyatts
; i
++)
11882 int pkattno
= indexStruct
->indkey
.values
[i
];
11884 attnums
[i
] = pkattno
;
11885 atttypids
[i
] = attnumTypeId(pkrel
, pkattno
);
11886 opclasses
[i
] = indclass
->values
[i
];
11887 *attnamelist
= lappend(*attnamelist
,
11888 makeString(pstrdup(NameStr(*attnumAttName(pkrel
, pkattno
)))));
11891 ReleaseSysCache(indexTuple
);
11897 * transformFkeyCheckAttrs -
11899 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
11900 * reference as part of a foreign key constraint.
11902 * Returns the OID of the unique index supporting the constraint and
11903 * populates the caller-provided 'opclasses' array with the opclasses
11904 * associated with the index columns.
11906 * Raises an ERROR on validation failure.
11909 transformFkeyCheckAttrs(Relation pkrel
,
11910 int numattrs
, int16
*attnums
,
11913 Oid indexoid
= InvalidOid
;
11914 bool found
= false;
11915 bool found_deferrable
= false;
11916 List
*indexoidlist
;
11917 ListCell
*indexoidscan
;
11922 * Reject duplicate appearances of columns in the referenced-columns list.
11923 * Such a case is forbidden by the SQL standard, and even if we thought it
11924 * useful to allow it, there would be ambiguity about how to match the
11925 * list to unique indexes (in particular, it'd be unclear which index
11926 * opclass goes with which FK column).
11928 for (i
= 0; i
< numattrs
; i
++)
11930 for (j
= i
+ 1; j
< numattrs
; j
++)
11932 if (attnums
[i
] == attnums
[j
])
11934 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
11935 errmsg("foreign key referenced-columns list must not contain duplicates")));
11940 * Get the list of index OIDs for the table from the relcache, and look up
11941 * each one in the pg_index syscache, and match unique indexes to the list
11942 * of attnums we are given.
11944 indexoidlist
= RelationGetIndexList(pkrel
);
11946 foreach(indexoidscan
, indexoidlist
)
11948 HeapTuple indexTuple
;
11949 Form_pg_index indexStruct
;
11951 indexoid
= lfirst_oid(indexoidscan
);
11952 indexTuple
= SearchSysCache1(INDEXRELID
, ObjectIdGetDatum(indexoid
));
11953 if (!HeapTupleIsValid(indexTuple
))
11954 elog(ERROR
, "cache lookup failed for index %u", indexoid
);
11955 indexStruct
= (Form_pg_index
) GETSTRUCT(indexTuple
);
11958 * Must have the right number of columns; must be unique and not a
11959 * partial index; forget it if there are any expressions, too. Invalid
11960 * indexes are out as well.
11962 if (indexStruct
->indnkeyatts
== numattrs
&&
11963 indexStruct
->indisunique
&&
11964 indexStruct
->indisvalid
&&
11965 heap_attisnull(indexTuple
, Anum_pg_index_indpred
, NULL
) &&
11966 heap_attisnull(indexTuple
, Anum_pg_index_indexprs
, NULL
))
11968 Datum indclassDatum
;
11969 oidvector
*indclass
;
11971 /* Must get indclass the hard way */
11972 indclassDatum
= SysCacheGetAttrNotNull(INDEXRELID
, indexTuple
,
11973 Anum_pg_index_indclass
);
11974 indclass
= (oidvector
*) DatumGetPointer(indclassDatum
);
11977 * The given attnum list may match the index columns in any order.
11978 * Check for a match, and extract the appropriate opclasses while
11981 * We know that attnums[] is duplicate-free per the test at the
11982 * start of this function, and we checked above that the number of
11983 * index columns agrees, so if we find a match for each attnums[]
11984 * entry then we must have a one-to-one match in some order.
11986 for (i
= 0; i
< numattrs
; i
++)
11989 for (j
= 0; j
< numattrs
; j
++)
11991 if (attnums
[i
] == indexStruct
->indkey
.values
[j
])
11993 opclasses
[i
] = indclass
->values
[j
];
12003 * Refuse to use a deferrable unique/primary key. This is per SQL
12004 * spec, and there would be a lot of interesting semantic problems
12005 * if we tried to allow it.
12007 if (found
&& !indexStruct
->indimmediate
)
12010 * Remember that we found an otherwise matching index, so that
12011 * we can generate a more appropriate error message.
12013 found_deferrable
= true;
12017 ReleaseSysCache(indexTuple
);
12024 if (found_deferrable
)
12026 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
12027 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12028 RelationGetRelationName(pkrel
))));
12031 (errcode(ERRCODE_INVALID_FOREIGN_KEY
),
12032 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12033 RelationGetRelationName(pkrel
))));
12036 list_free(indexoidlist
);
12044 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12045 * Caller has equal regard for binary coercibility and for an exact match.
12047 static CoercionPathType
12048 findFkeyCast(Oid targetTypeId
, Oid sourceTypeId
, Oid
*funcid
)
12050 CoercionPathType ret
;
12052 if (targetTypeId
== sourceTypeId
)
12054 ret
= COERCION_PATH_RELABELTYPE
;
12055 *funcid
= InvalidOid
;
12059 ret
= find_coercion_pathway(targetTypeId
, sourceTypeId
,
12060 COERCION_IMPLICIT
, funcid
);
12061 if (ret
== COERCION_PATH_NONE
)
12062 /* A previously-relied-upon cast is now gone. */
12063 elog(ERROR
, "could not find cast from %u to %u",
12064 sourceTypeId
, targetTypeId
);
12071 * Permissions checks on the referenced table for ADD FOREIGN KEY
12073 * Note: we have already checked that the user owns the referencing table,
12074 * else we'd have failed much earlier; no additional checks are needed for it.
12077 checkFkeyPermissions(Relation rel
, int16
*attnums
, int natts
)
12079 Oid roleid
= GetUserId();
12080 AclResult aclresult
;
12083 /* Okay if we have relation-level REFERENCES permission */
12084 aclresult
= pg_class_aclcheck(RelationGetRelid(rel
), roleid
,
12086 if (aclresult
== ACLCHECK_OK
)
12088 /* Else we must have REFERENCES on each column */
12089 for (i
= 0; i
< natts
; i
++)
12091 aclresult
= pg_attribute_aclcheck(RelationGetRelid(rel
), attnums
[i
],
12092 roleid
, ACL_REFERENCES
);
12093 if (aclresult
!= ACLCHECK_OK
)
12094 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
),
12095 RelationGetRelationName(rel
));
12100 * Scan the existing rows in a table to verify they meet a proposed FK
12103 * Caller must have opened and locked both relations appropriately.
12106 validateForeignKeyConstraint(char *conname
,
12112 TupleTableSlot
*slot
;
12113 TableScanDesc scan
;
12114 Trigger trig
= {0};
12116 MemoryContext oldcxt
;
12117 MemoryContext perTupCxt
;
12120 (errmsg_internal("validating foreign key constraint \"%s\"", conname
)));
12123 * Build a trigger call structure; we'll need it either way.
12125 trig
.tgoid
= InvalidOid
;
12126 trig
.tgname
= conname
;
12127 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
12128 trig
.tgisinternal
= true;
12129 trig
.tgconstrrelid
= RelationGetRelid(pkrel
);
12130 trig
.tgconstrindid
= pkindOid
;
12131 trig
.tgconstraint
= constraintOid
;
12132 trig
.tgdeferrable
= false;
12133 trig
.tginitdeferred
= false;
12134 /* we needn't fill in remaining fields */
12137 * See if we can do it with a single LEFT JOIN query. A false result
12138 * indicates we must proceed with the fire-the-trigger method.
12140 if (RI_Initial_Check(&trig
, rel
, pkrel
))
12144 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12145 * if that tuple had just been inserted. If any of those fail, it should
12146 * ereport(ERROR) and that's that.
12148 snapshot
= RegisterSnapshot(GetLatestSnapshot());
12149 slot
= table_slot_create(rel
, NULL
);
12150 scan
= table_beginscan(rel
, snapshot
, 0, NULL
);
12152 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
12153 "validateForeignKeyConstraint",
12154 ALLOCSET_SMALL_SIZES
);
12155 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
12157 while (table_scan_getnextslot(scan
, ForwardScanDirection
, slot
))
12159 LOCAL_FCINFO(fcinfo
, 0);
12160 TriggerData trigdata
= {0};
12162 CHECK_FOR_INTERRUPTS();
12165 * Make a call to the trigger function
12167 * No parameters are passed, but we do set a context
12169 MemSet(fcinfo
, 0, SizeForFunctionCallInfo(0));
12172 * We assume RI_FKey_check_ins won't look at flinfo...
12174 trigdata
.type
= T_TriggerData
;
12175 trigdata
.tg_event
= TRIGGER_EVENT_INSERT
| TRIGGER_EVENT_ROW
;
12176 trigdata
.tg_relation
= rel
;
12177 trigdata
.tg_trigtuple
= ExecFetchSlotHeapTuple(slot
, false, NULL
);
12178 trigdata
.tg_trigslot
= slot
;
12179 trigdata
.tg_trigger
= &trig
;
12181 fcinfo
->context
= (Node
*) &trigdata
;
12183 RI_FKey_check_ins(fcinfo
);
12185 MemoryContextReset(perTupCxt
);
12188 MemoryContextSwitchTo(oldcxt
);
12189 MemoryContextDelete(perTupCxt
);
12190 table_endscan(scan
);
12191 UnregisterSnapshot(snapshot
);
12192 ExecDropSingleTupleTableSlot(slot
);
12196 * CreateFKCheckTrigger
12197 * Creates the insert (on_insert=true) or update "check" trigger that
12198 * implements a given foreign key
12200 * Returns the OID of the so created trigger.
12203 CreateFKCheckTrigger(Oid myRelOid
, Oid refRelOid
, Constraint
*fkconstraint
,
12204 Oid constraintOid
, Oid indexOid
, Oid parentTrigOid
,
12207 ObjectAddress trigAddress
;
12208 CreateTrigStmt
*fk_trigger
;
12211 * Note: for a self-referential FK (referencing and referenced tables are
12212 * the same), it is important that the ON UPDATE action fires before the
12213 * CHECK action, since both triggers will fire on the same row during an
12214 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12215 * state of the row. Triggers fire in name order, so we ensure this by
12216 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12217 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12219 fk_trigger
= makeNode(CreateTrigStmt
);
12220 fk_trigger
->replace
= false;
12221 fk_trigger
->isconstraint
= true;
12222 fk_trigger
->trigname
= "RI_ConstraintTrigger_c";
12223 fk_trigger
->relation
= NULL
;
12225 /* Either ON INSERT or ON UPDATE */
12228 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_ins");
12229 fk_trigger
->events
= TRIGGER_TYPE_INSERT
;
12233 fk_trigger
->funcname
= SystemFuncName("RI_FKey_check_upd");
12234 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12237 fk_trigger
->args
= NIL
;
12238 fk_trigger
->row
= true;
12239 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12240 fk_trigger
->columns
= NIL
;
12241 fk_trigger
->whenClause
= NULL
;
12242 fk_trigger
->transitionRels
= NIL
;
12243 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12244 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12245 fk_trigger
->constrrel
= NULL
;
12247 trigAddress
= CreateTrigger(fk_trigger
, NULL
, myRelOid
, refRelOid
,
12248 constraintOid
, indexOid
, InvalidOid
,
12249 parentTrigOid
, NULL
, true, false);
12251 /* Make changes-so-far visible */
12252 CommandCounterIncrement();
12254 return trigAddress
.objectId
;
12258 * createForeignKeyActionTriggers
12259 * Create the referenced-side "action" triggers that implement a foreign
12262 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12266 createForeignKeyActionTriggers(Relation rel
, Oid refRelOid
, Constraint
*fkconstraint
,
12267 Oid constraintOid
, Oid indexOid
,
12268 Oid parentDelTrigger
, Oid parentUpdTrigger
,
12269 Oid
*deleteTrigOid
, Oid
*updateTrigOid
)
12271 CreateTrigStmt
*fk_trigger
;
12272 ObjectAddress trigAddress
;
12275 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12276 * DELETE action on the referenced table.
12278 fk_trigger
= makeNode(CreateTrigStmt
);
12279 fk_trigger
->replace
= false;
12280 fk_trigger
->isconstraint
= true;
12281 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12282 fk_trigger
->relation
= NULL
;
12283 fk_trigger
->args
= NIL
;
12284 fk_trigger
->row
= true;
12285 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12286 fk_trigger
->events
= TRIGGER_TYPE_DELETE
;
12287 fk_trigger
->columns
= NIL
;
12288 fk_trigger
->whenClause
= NULL
;
12289 fk_trigger
->transitionRels
= NIL
;
12290 fk_trigger
->constrrel
= NULL
;
12291 switch (fkconstraint
->fk_del_action
)
12293 case FKCONSTR_ACTION_NOACTION
:
12294 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12295 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12296 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_del");
12298 case FKCONSTR_ACTION_RESTRICT
:
12299 fk_trigger
->deferrable
= false;
12300 fk_trigger
->initdeferred
= false;
12301 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_del");
12303 case FKCONSTR_ACTION_CASCADE
:
12304 fk_trigger
->deferrable
= false;
12305 fk_trigger
->initdeferred
= false;
12306 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_del");
12308 case FKCONSTR_ACTION_SETNULL
:
12309 fk_trigger
->deferrable
= false;
12310 fk_trigger
->initdeferred
= false;
12311 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_del");
12313 case FKCONSTR_ACTION_SETDEFAULT
:
12314 fk_trigger
->deferrable
= false;
12315 fk_trigger
->initdeferred
= false;
12316 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_del");
12319 elog(ERROR
, "unrecognized FK action type: %d",
12320 (int) fkconstraint
->fk_del_action
);
12324 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12325 RelationGetRelid(rel
),
12326 constraintOid
, indexOid
, InvalidOid
,
12327 parentDelTrigger
, NULL
, true, false);
12329 *deleteTrigOid
= trigAddress
.objectId
;
12331 /* Make changes-so-far visible */
12332 CommandCounterIncrement();
12335 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12336 * UPDATE action on the referenced table.
12338 fk_trigger
= makeNode(CreateTrigStmt
);
12339 fk_trigger
->replace
= false;
12340 fk_trigger
->isconstraint
= true;
12341 fk_trigger
->trigname
= "RI_ConstraintTrigger_a";
12342 fk_trigger
->relation
= NULL
;
12343 fk_trigger
->args
= NIL
;
12344 fk_trigger
->row
= true;
12345 fk_trigger
->timing
= TRIGGER_TYPE_AFTER
;
12346 fk_trigger
->events
= TRIGGER_TYPE_UPDATE
;
12347 fk_trigger
->columns
= NIL
;
12348 fk_trigger
->whenClause
= NULL
;
12349 fk_trigger
->transitionRels
= NIL
;
12350 fk_trigger
->constrrel
= NULL
;
12351 switch (fkconstraint
->fk_upd_action
)
12353 case FKCONSTR_ACTION_NOACTION
:
12354 fk_trigger
->deferrable
= fkconstraint
->deferrable
;
12355 fk_trigger
->initdeferred
= fkconstraint
->initdeferred
;
12356 fk_trigger
->funcname
= SystemFuncName("RI_FKey_noaction_upd");
12358 case FKCONSTR_ACTION_RESTRICT
:
12359 fk_trigger
->deferrable
= false;
12360 fk_trigger
->initdeferred
= false;
12361 fk_trigger
->funcname
= SystemFuncName("RI_FKey_restrict_upd");
12363 case FKCONSTR_ACTION_CASCADE
:
12364 fk_trigger
->deferrable
= false;
12365 fk_trigger
->initdeferred
= false;
12366 fk_trigger
->funcname
= SystemFuncName("RI_FKey_cascade_upd");
12368 case FKCONSTR_ACTION_SETNULL
:
12369 fk_trigger
->deferrable
= false;
12370 fk_trigger
->initdeferred
= false;
12371 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setnull_upd");
12373 case FKCONSTR_ACTION_SETDEFAULT
:
12374 fk_trigger
->deferrable
= false;
12375 fk_trigger
->initdeferred
= false;
12376 fk_trigger
->funcname
= SystemFuncName("RI_FKey_setdefault_upd");
12379 elog(ERROR
, "unrecognized FK action type: %d",
12380 (int) fkconstraint
->fk_upd_action
);
12384 trigAddress
= CreateTrigger(fk_trigger
, NULL
, refRelOid
,
12385 RelationGetRelid(rel
),
12386 constraintOid
, indexOid
, InvalidOid
,
12387 parentUpdTrigger
, NULL
, true, false);
12389 *updateTrigOid
= trigAddress
.objectId
;
12393 * createForeignKeyCheckTriggers
12394 * Create the referencing-side "check" triggers that implement a foreign
12397 * Returns the OIDs of the so created triggers in *insertTrigOid and
12401 createForeignKeyCheckTriggers(Oid myRelOid
, Oid refRelOid
,
12402 Constraint
*fkconstraint
, Oid constraintOid
,
12404 Oid parentInsTrigger
, Oid parentUpdTrigger
,
12405 Oid
*insertTrigOid
, Oid
*updateTrigOid
)
12407 *insertTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12408 constraintOid
, indexOid
,
12409 parentInsTrigger
, true);
12410 *updateTrigOid
= CreateFKCheckTrigger(myRelOid
, refRelOid
, fkconstraint
,
12411 constraintOid
, indexOid
,
12412 parentUpdTrigger
, false);
12416 * ALTER TABLE DROP CONSTRAINT
12418 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12421 ATExecDropConstraint(Relation rel
, const char *constrName
,
12422 DropBehavior behavior
,
12423 bool recurse
, bool recursing
,
12424 bool missing_ok
, LOCKMODE lockmode
)
12428 Form_pg_constraint con
;
12430 ScanKeyData skey
[3];
12432 bool found
= false;
12433 bool is_no_inherit_constraint
= false;
12436 /* At top level, permission check was done in ATPrepCmd, else do it */
12438 ATSimplePermissions(AT_DropConstraint
, rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
12440 conrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
12443 * Find and drop the target constraint
12445 ScanKeyInit(&skey
[0],
12446 Anum_pg_constraint_conrelid
,
12447 BTEqualStrategyNumber
, F_OIDEQ
,
12448 ObjectIdGetDatum(RelationGetRelid(rel
)));
12449 ScanKeyInit(&skey
[1],
12450 Anum_pg_constraint_contypid
,
12451 BTEqualStrategyNumber
, F_OIDEQ
,
12452 ObjectIdGetDatum(InvalidOid
));
12453 ScanKeyInit(&skey
[2],
12454 Anum_pg_constraint_conname
,
12455 BTEqualStrategyNumber
, F_NAMEEQ
,
12456 CStringGetDatum(constrName
));
12457 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12458 true, NULL
, 3, skey
);
12460 /* There can be at most one matching row */
12461 if (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12463 ObjectAddress conobj
;
12465 con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
12467 /* Don't drop inherited constraints */
12468 if (con
->coninhcount
> 0 && !recursing
)
12470 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12471 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12472 constrName
, RelationGetRelationName(rel
))));
12474 is_no_inherit_constraint
= con
->connoinherit
;
12475 contype
= con
->contype
;
12478 * If it's a foreign-key constraint, we'd better lock the referenced
12479 * table and check that that's not in use, just as we've already done
12480 * for the constrained table (else we might, eg, be dropping a trigger
12481 * that has unfired events). But we can/must skip that in the
12482 * self-referential case.
12484 if (contype
== CONSTRAINT_FOREIGN
&&
12485 con
->confrelid
!= RelationGetRelid(rel
))
12489 /* Must match lock taken by RemoveTriggerById: */
12490 frel
= table_open(con
->confrelid
, AccessExclusiveLock
);
12491 CheckAlterTableIsSafe(frel
);
12492 table_close(frel
, NoLock
);
12496 * Perform the actual constraint deletion
12498 conobj
.classId
= ConstraintRelationId
;
12499 conobj
.objectId
= con
->oid
;
12500 conobj
.objectSubId
= 0;
12502 performDeletion(&conobj
, behavior
, 0);
12507 systable_endscan(scan
);
12514 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12515 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12516 constrName
, RelationGetRelationName(rel
))));
12521 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12522 constrName
, RelationGetRelationName(rel
))));
12523 table_close(conrel
, RowExclusiveLock
);
12529 * For partitioned tables, non-CHECK inherited constraints are dropped via
12530 * the dependency mechanism, so we're done here.
12532 if (contype
!= CONSTRAINT_CHECK
&&
12533 rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
12535 table_close(conrel
, RowExclusiveLock
);
12540 * Propagate to children as appropriate. Unlike most other ALTER
12541 * routines, we have to do this one level of recursion at a time; we can't
12542 * use find_all_inheritors to do it in one pass.
12544 if (!is_no_inherit_constraint
)
12545 children
= find_inheritance_children(RelationGetRelid(rel
), lockmode
);
12550 * For a partitioned table, if partitions exist and we are told not to
12551 * recurse, it's a user error. It doesn't make sense to have a constraint
12552 * be defined only on the parent, especially if it's a partitioned table.
12554 if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
&&
12555 children
!= NIL
&& !recurse
)
12557 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12558 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12559 errhint("Do not specify the ONLY keyword.")));
12561 foreach_oid(childrelid
, children
)
12564 HeapTuple copy_tuple
;
12566 /* find_inheritance_children already got lock */
12567 childrel
= table_open(childrelid
, NoLock
);
12568 CheckAlterTableIsSafe(childrel
);
12570 ScanKeyInit(&skey
[0],
12571 Anum_pg_constraint_conrelid
,
12572 BTEqualStrategyNumber
, F_OIDEQ
,
12573 ObjectIdGetDatum(childrelid
));
12574 ScanKeyInit(&skey
[1],
12575 Anum_pg_constraint_contypid
,
12576 BTEqualStrategyNumber
, F_OIDEQ
,
12577 ObjectIdGetDatum(InvalidOid
));
12578 ScanKeyInit(&skey
[2],
12579 Anum_pg_constraint_conname
,
12580 BTEqualStrategyNumber
, F_NAMEEQ
,
12581 CStringGetDatum(constrName
));
12582 scan
= systable_beginscan(conrel
, ConstraintRelidTypidNameIndexId
,
12583 true, NULL
, 3, skey
);
12585 /* There can be at most one matching row */
12586 if (!HeapTupleIsValid(tuple
= systable_getnext(scan
)))
12588 (errcode(ERRCODE_UNDEFINED_OBJECT
),
12589 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12591 RelationGetRelationName(childrel
))));
12593 copy_tuple
= heap_copytuple(tuple
);
12595 systable_endscan(scan
);
12597 con
= (Form_pg_constraint
) GETSTRUCT(copy_tuple
);
12599 /* Right now only CHECK constraints can be inherited */
12600 if (con
->contype
!= CONSTRAINT_CHECK
)
12601 elog(ERROR
, "inherited constraint is not a CHECK constraint");
12603 if (con
->coninhcount
<= 0) /* shouldn't happen */
12604 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
12605 childrelid
, constrName
);
12610 * If the child constraint has other definition sources, just
12611 * decrement its inheritance count; if not, recurse to delete it.
12613 if (con
->coninhcount
== 1 && !con
->conislocal
)
12615 /* Time to delete this child constraint, too */
12616 ATExecDropConstraint(childrel
, constrName
, behavior
,
12622 /* Child constraint must survive my deletion */
12623 con
->coninhcount
--;
12624 CatalogTupleUpdate(conrel
, ©_tuple
->t_self
, copy_tuple
);
12626 /* Make update visible */
12627 CommandCounterIncrement();
12633 * If we were told to drop ONLY in this table (no recursion), we
12634 * need to mark the inheritors' constraints as locally defined
12635 * rather than inherited.
12637 con
->coninhcount
--;
12638 con
->conislocal
= true;
12640 CatalogTupleUpdate(conrel
, ©_tuple
->t_self
, copy_tuple
);
12642 /* Make update visible */
12643 CommandCounterIncrement();
12646 heap_freetuple(copy_tuple
);
12648 table_close(childrel
, NoLock
);
12651 table_close(conrel
, RowExclusiveLock
);
12655 * ALTER COLUMN TYPE
12657 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12658 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12659 * transformed (and must be, because we rely on some transformed fields).
12661 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12662 * table will be done "in parallel" during phase 3, so all the USING
12663 * expressions should be parsed assuming the original column types. Also,
12664 * this allows a USING expression to refer to a field that will be dropped.
12666 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12667 * the first two execution steps in phase 2; they must not see the effects
12668 * of any other subcommand types, since the USING expressions are parsed
12669 * against the unmodified table's state.
12672 ATPrepAlterColumnType(List
**wqueue
,
12673 AlteredTableInfo
*tab
, Relation rel
,
12674 bool recurse
, bool recursing
,
12675 AlterTableCmd
*cmd
, LOCKMODE lockmode
,
12676 AlterTableUtilityContext
*context
)
12678 char *colName
= cmd
->name
;
12679 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
12680 TypeName
*typeName
= def
->typeName
;
12681 Node
*transform
= def
->cooked_default
;
12683 Form_pg_attribute attTup
;
12686 int32 targettypmod
;
12688 NewColumnValue
*newval
;
12689 ParseState
*pstate
= make_parsestate(NULL
);
12690 AclResult aclresult
;
12693 if (rel
->rd_rel
->reloftype
&& !recursing
)
12695 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12696 errmsg("cannot alter column type of typed table")));
12698 /* lookup the attribute so we can check inheritance status */
12699 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
12700 if (!HeapTupleIsValid(tuple
))
12702 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12703 errmsg("column \"%s\" of relation \"%s\" does not exist",
12704 colName
, RelationGetRelationName(rel
))));
12705 attTup
= (Form_pg_attribute
) GETSTRUCT(tuple
);
12706 attnum
= attTup
->attnum
;
12708 /* Can't alter a system attribute */
12711 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12712 errmsg("cannot alter system column \"%s\"",
12716 * Don't alter inherited columns. At outer level, there had better not be
12717 * any inherited definition; when recursing, we assume this was checked at
12718 * the parent level (see below).
12720 if (attTup
->attinhcount
> 0 && !recursing
)
12722 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12723 errmsg("cannot alter inherited column \"%s\"",
12726 /* Don't alter columns used in the partition key */
12727 if (has_partition_attrs(rel
,
12728 bms_make_singleton(attnum
- FirstLowInvalidHeapAttributeNumber
),
12731 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12732 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12733 colName
, RelationGetRelationName(rel
))));
12735 /* Look up the target type */
12736 typenameTypeIdAndMod(NULL
, typeName
, &targettype
, &targettypmod
);
12738 aclresult
= object_aclcheck(TypeRelationId
, targettype
, GetUserId(), ACL_USAGE
);
12739 if (aclresult
!= ACLCHECK_OK
)
12740 aclcheck_error_type(aclresult
, targettype
);
12742 /* And the collation */
12743 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
12745 /* make sure datatype is legal for a column */
12746 CheckAttributeType(colName
, targettype
, targetcollid
,
12747 list_make1_oid(rel
->rd_rel
->reltype
),
12750 if (tab
->relkind
== RELKIND_RELATION
||
12751 tab
->relkind
== RELKIND_PARTITIONED_TABLE
)
12754 * Set up an expression to transform the old data value to the new
12755 * type. If a USING option was given, use the expression as
12756 * transformed by transformAlterTableStmt, else just take the old
12757 * value and try to coerce it. We do this first so that type
12758 * incompatibility can be detected before we waste effort, and because
12759 * we need the expression to be parsed against the original table row
12764 transform
= (Node
*) makeVar(1, attnum
,
12765 attTup
->atttypid
, attTup
->atttypmod
,
12766 attTup
->attcollation
,
12770 transform
= coerce_to_target_type(pstate
,
12771 transform
, exprType(transform
),
12772 targettype
, targettypmod
,
12773 COERCION_ASSIGNMENT
,
12774 COERCE_IMPLICIT_CAST
,
12776 if (transform
== NULL
)
12778 /* error text depends on whether USING was specified or not */
12779 if (def
->cooked_default
!= NULL
)
12781 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12782 errmsg("result of USING clause for column \"%s\""
12783 " cannot be cast automatically to type %s",
12784 colName
, format_type_be(targettype
)),
12785 errhint("You might need to add an explicit cast.")));
12788 (errcode(ERRCODE_DATATYPE_MISMATCH
),
12789 errmsg("column \"%s\" cannot be cast automatically to type %s",
12790 colName
, format_type_be(targettype
)),
12791 /* translator: USING is SQL, don't translate it */
12792 errhint("You might need to specify \"USING %s::%s\".",
12793 quote_identifier(colName
),
12794 format_type_with_typemod(targettype
,
12798 /* Fix collations after all else */
12799 assign_expr_collations(pstate
, transform
);
12801 /* Plan the expr now so we can accurately assess the need to rewrite. */
12802 transform
= (Node
*) expression_planner((Expr
*) transform
);
12805 * Add a work queue item to make ATRewriteTable update the column
12808 newval
= (NewColumnValue
*) palloc0(sizeof(NewColumnValue
));
12809 newval
->attnum
= attnum
;
12810 newval
->expr
= (Expr
*) transform
;
12811 newval
->is_generated
= false;
12813 tab
->newvals
= lappend(tab
->newvals
, newval
);
12814 if (ATColumnChangeRequiresRewrite(transform
, attnum
))
12815 tab
->rewrite
|= AT_REWRITE_COLUMN_REWRITE
;
12817 else if (transform
)
12819 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
12820 errmsg("\"%s\" is not a table",
12821 RelationGetRelationName(rel
))));
12823 if (!RELKIND_HAS_STORAGE(tab
->relkind
))
12826 * For relations without storage, do this check now. Regular tables
12827 * will check it later when the table is being rewritten.
12829 find_composite_type_dependencies(rel
->rd_rel
->reltype
, rel
, NULL
);
12832 ReleaseSysCache(tuple
);
12835 * Recurse manually by queueing a new command for each child, if
12836 * necessary. We cannot apply ATSimpleRecursion here because we need to
12837 * remap attribute numbers in the USING expression, if any.
12839 * If we are told not to recurse, there had better not be any child
12840 * tables; else the alter would put them out of step.
12844 Oid relid
= RelationGetRelid(rel
);
12850 child_oids
= find_all_inheritors(relid
, lockmode
,
12851 &child_numparents
);
12854 * find_all_inheritors does the recursive search of the inheritance
12855 * hierarchy, so all we have to do is process all of the relids in the
12856 * list that it returns.
12858 forboth(lo
, child_oids
, li
, child_numparents
)
12860 Oid childrelid
= lfirst_oid(lo
);
12861 int numparents
= lfirst_int(li
);
12863 HeapTuple childtuple
;
12864 Form_pg_attribute childattTup
;
12866 if (childrelid
== relid
)
12869 /* find_all_inheritors already got lock */
12870 childrel
= relation_open(childrelid
, NoLock
);
12871 CheckAlterTableIsSafe(childrel
);
12874 * Verify that the child doesn't have any inherited definitions of
12875 * this column that came from outside this inheritance hierarchy.
12876 * (renameatt makes a similar test, though in a different way
12877 * because of its different recursion mechanism.)
12879 childtuple
= SearchSysCacheAttName(RelationGetRelid(childrel
),
12881 if (!HeapTupleIsValid(childtuple
))
12883 (errcode(ERRCODE_UNDEFINED_COLUMN
),
12884 errmsg("column \"%s\" of relation \"%s\" does not exist",
12885 colName
, RelationGetRelationName(childrel
))));
12886 childattTup
= (Form_pg_attribute
) GETSTRUCT(childtuple
);
12888 if (childattTup
->attinhcount
> numparents
)
12890 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12891 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12892 colName
, RelationGetRelationName(childrel
))));
12894 ReleaseSysCache(childtuple
);
12897 * Remap the attribute numbers. If no USING expression was
12898 * specified, there is no need for this step.
12900 if (def
->cooked_default
)
12903 bool found_whole_row
;
12905 /* create a copy to scribble on */
12906 cmd
= copyObject(cmd
);
12908 attmap
= build_attrmap_by_name(RelationGetDescr(childrel
),
12909 RelationGetDescr(rel
),
12911 ((ColumnDef
*) cmd
->def
)->cooked_default
=
12912 map_variable_attnos(def
->cooked_default
,
12915 InvalidOid
, &found_whole_row
);
12916 if (found_whole_row
)
12918 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
12919 errmsg("cannot convert whole-row table reference"),
12920 errdetail("USING expression contains a whole-row table reference.")));
12923 ATPrepCmd(wqueue
, childrel
, cmd
, false, true, lockmode
, context
);
12924 relation_close(childrel
, NoLock
);
12927 else if (!recursing
&&
12928 find_inheritance_children(RelationGetRelid(rel
), NoLock
) != NIL
)
12930 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
12931 errmsg("type of inherited column \"%s\" must be changed in child tables too",
12934 if (tab
->relkind
== RELKIND_COMPOSITE_TYPE
)
12935 ATTypedTableRecursion(wqueue
, rel
, cmd
, lockmode
, context
);
12939 * When the data type of a column is changed, a rewrite might not be required
12940 * if the new type is sufficiently identical to the old one, and the USING
12941 * clause isn't trying to insert some other value. It's safe to skip the
12942 * rewrite in these cases:
12944 * - the old type is binary coercible to the new type
12945 * - the new type is an unconstrained domain over the old type
12946 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
12948 * In the case of a constrained domain, we could get by with scanning the
12949 * table and checking the constraint rather than actually rewriting it, but we
12950 * don't currently try to do that.
12953 ATColumnChangeRequiresRewrite(Node
*expr
, AttrNumber varattno
)
12955 Assert(expr
!= NULL
);
12959 /* only one varno, so no need to check that */
12960 if (IsA(expr
, Var
) && ((Var
*) expr
)->varattno
== varattno
)
12962 else if (IsA(expr
, RelabelType
))
12963 expr
= (Node
*) ((RelabelType
*) expr
)->arg
;
12964 else if (IsA(expr
, CoerceToDomain
))
12966 CoerceToDomain
*d
= (CoerceToDomain
*) expr
;
12968 if (DomainHasConstraints(d
->resulttype
))
12970 expr
= (Node
*) d
->arg
;
12972 else if (IsA(expr
, FuncExpr
))
12974 FuncExpr
*f
= (FuncExpr
*) expr
;
12978 case F_TIMESTAMPTZ_TIMESTAMP
:
12979 case F_TIMESTAMP_TIMESTAMPTZ
:
12980 if (TimestampTimestampTzRequiresRewrite())
12983 expr
= linitial(f
->args
);
12995 * ALTER COLUMN .. SET DATA TYPE
12997 * Return the address of the modified column.
12999 static ObjectAddress
13000 ATExecAlterColumnType(AlteredTableInfo
*tab
, Relation rel
,
13001 AlterTableCmd
*cmd
, LOCKMODE lockmode
)
13003 char *colName
= cmd
->name
;
13004 ColumnDef
*def
= (ColumnDef
*) cmd
->def
;
13005 TypeName
*typeName
= def
->typeName
;
13007 Form_pg_attribute attTup
,
13010 HeapTuple typeTuple
;
13011 Form_pg_type tform
;
13013 int32 targettypmod
;
13016 Relation attrelation
;
13018 ScanKeyData key
[3];
13021 ObjectAddress address
;
13024 * Clear all the missing values if we're rewriting the table, since this
13025 * renders them pointless.
13031 newrel
= table_open(RelationGetRelid(rel
), NoLock
);
13032 RelationClearMissing(newrel
);
13033 relation_close(newrel
, NoLock
);
13034 /* make sure we don't conflict with later attribute modifications */
13035 CommandCounterIncrement();
13038 attrelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
13040 /* Look up the target column */
13041 heapTup
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), colName
);
13042 if (!HeapTupleIsValid(heapTup
)) /* shouldn't happen */
13044 (errcode(ERRCODE_UNDEFINED_COLUMN
),
13045 errmsg("column \"%s\" of relation \"%s\" does not exist",
13046 colName
, RelationGetRelationName(rel
))));
13047 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13048 attnum
= attTup
->attnum
;
13049 attOldTup
= TupleDescAttr(tab
->oldDesc
, attnum
- 1);
13051 /* Check for multiple ALTER TYPE on same column --- can't cope */
13052 if (attTup
->atttypid
!= attOldTup
->atttypid
||
13053 attTup
->atttypmod
!= attOldTup
->atttypmod
)
13055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13056 errmsg("cannot alter type of column \"%s\" twice",
13059 /* Look up the target type (should not fail, since prep found it) */
13060 typeTuple
= typenameType(NULL
, typeName
, &targettypmod
);
13061 tform
= (Form_pg_type
) GETSTRUCT(typeTuple
);
13062 targettype
= tform
->oid
;
13063 /* And the collation */
13064 targetcollid
= GetColumnDefCollation(NULL
, def
, targettype
);
13067 * If there is a default expression for the column, get it and ensure we
13068 * can coerce it to the new datatype. (We must do this before changing
13069 * the column type, because build_column_default itself will try to
13070 * coerce, and will not issue the error message we want if it fails.)
13072 * We remove any implicit coercion steps at the top level of the old
13073 * default expression; this has been agreed to satisfy the principle of
13074 * least surprise. (The conversion to the new column type should act like
13075 * it started from what the user sees as the stored expression, and the
13076 * implicit coercions aren't going to be shown.)
13078 if (attTup
->atthasdef
)
13080 defaultexpr
= build_column_default(rel
, attnum
);
13081 Assert(defaultexpr
);
13082 defaultexpr
= strip_implicit_coercions(defaultexpr
);
13083 defaultexpr
= coerce_to_target_type(NULL
, /* no UNKNOWN params */
13084 defaultexpr
, exprType(defaultexpr
),
13085 targettype
, targettypmod
,
13086 COERCION_ASSIGNMENT
,
13087 COERCE_IMPLICIT_CAST
,
13089 if (defaultexpr
== NULL
)
13091 if (attTup
->attgenerated
)
13093 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13094 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13095 colName
, format_type_be(targettype
))));
13098 (errcode(ERRCODE_DATATYPE_MISMATCH
),
13099 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13100 colName
, format_type_be(targettype
))));
13104 defaultexpr
= NULL
;
13107 * Find everything that depends on the column (constraints, indexes, etc),
13108 * and record enough information to let us recreate the objects.
13110 * The actual recreation does not happen here, but only after we have
13111 * performed all the individual ALTER TYPE operations. We have to save
13112 * the info before executing ALTER TYPE, though, else the deparser will
13115 RememberAllDependentForRebuilding(tab
, AT_AlterColumnType
, rel
, attnum
, colName
);
13118 * Now scan for dependencies of this column on other things. The only
13119 * things we should find are the dependency on the column datatype and
13120 * possibly a collation dependency. Those can be removed.
13122 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13124 ScanKeyInit(&key
[0],
13125 Anum_pg_depend_classid
,
13126 BTEqualStrategyNumber
, F_OIDEQ
,
13127 ObjectIdGetDatum(RelationRelationId
));
13128 ScanKeyInit(&key
[1],
13129 Anum_pg_depend_objid
,
13130 BTEqualStrategyNumber
, F_OIDEQ
,
13131 ObjectIdGetDatum(RelationGetRelid(rel
)));
13132 ScanKeyInit(&key
[2],
13133 Anum_pg_depend_objsubid
,
13134 BTEqualStrategyNumber
, F_INT4EQ
,
13135 Int32GetDatum((int32
) attnum
));
13137 scan
= systable_beginscan(depRel
, DependDependerIndexId
, true,
13140 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13142 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13143 ObjectAddress foundObject
;
13145 foundObject
.classId
= foundDep
->refclassid
;
13146 foundObject
.objectId
= foundDep
->refobjid
;
13147 foundObject
.objectSubId
= foundDep
->refobjsubid
;
13149 if (foundDep
->deptype
!= DEPENDENCY_NORMAL
)
13150 elog(ERROR
, "found unexpected dependency type '%c'",
13151 foundDep
->deptype
);
13152 if (!(foundDep
->refclassid
== TypeRelationId
&&
13153 foundDep
->refobjid
== attTup
->atttypid
) &&
13154 !(foundDep
->refclassid
== CollationRelationId
&&
13155 foundDep
->refobjid
== attTup
->attcollation
))
13156 elog(ERROR
, "found unexpected dependency for column: %s",
13157 getObjectDescription(&foundObject
, false));
13159 CatalogTupleDelete(depRel
, &depTup
->t_self
);
13162 systable_endscan(scan
);
13164 table_close(depRel
, RowExclusiveLock
);
13167 * Here we go --- change the recorded column type and collation. (Note
13168 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13169 * fix up the missing value if any.
13171 if (attTup
->atthasmissing
)
13176 /* if rewrite is true the missing value should already be cleared */
13177 Assert(tab
->rewrite
== 0);
13179 /* Get the missing value datum */
13180 missingval
= heap_getattr(heapTup
,
13181 Anum_pg_attribute_attmissingval
,
13182 attrelation
->rd_att
,
13185 /* if it's a null array there is nothing to do */
13190 * Get the datum out of the array and repack it in a new array
13191 * built with the new type data. We assume that since the table
13192 * doesn't need rewriting, the actual Datum doesn't need to be
13193 * changed, only the array metadata.
13198 Datum valuesAtt
[Natts_pg_attribute
] = {0};
13199 bool nullsAtt
[Natts_pg_attribute
] = {0};
13200 bool replacesAtt
[Natts_pg_attribute
] = {0};
13203 missingval
= array_get_element(missingval
,
13211 missingval
= PointerGetDatum(construct_array(&missingval
,
13218 valuesAtt
[Anum_pg_attribute_attmissingval
- 1] = missingval
;
13219 replacesAtt
[Anum_pg_attribute_attmissingval
- 1] = true;
13220 nullsAtt
[Anum_pg_attribute_attmissingval
- 1] = false;
13222 newTup
= heap_modify_tuple(heapTup
, RelationGetDescr(attrelation
),
13223 valuesAtt
, nullsAtt
, replacesAtt
);
13224 heap_freetuple(heapTup
);
13226 attTup
= (Form_pg_attribute
) GETSTRUCT(heapTup
);
13230 attTup
->atttypid
= targettype
;
13231 attTup
->atttypmod
= targettypmod
;
13232 attTup
->attcollation
= targetcollid
;
13233 if (list_length(typeName
->arrayBounds
) > PG_INT16_MAX
)
13235 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
13236 errmsg("too many array dimensions"));
13237 attTup
->attndims
= list_length(typeName
->arrayBounds
);
13238 attTup
->attlen
= tform
->typlen
;
13239 attTup
->attbyval
= tform
->typbyval
;
13240 attTup
->attalign
= tform
->typalign
;
13241 attTup
->attstorage
= tform
->typstorage
;
13242 attTup
->attcompression
= InvalidCompressionMethod
;
13244 ReleaseSysCache(typeTuple
);
13246 CatalogTupleUpdate(attrelation
, &heapTup
->t_self
, heapTup
);
13248 table_close(attrelation
, RowExclusiveLock
);
13250 /* Install dependencies on new datatype and collation */
13251 add_column_datatype_dependency(RelationGetRelid(rel
), attnum
, targettype
);
13252 add_column_collation_dependency(RelationGetRelid(rel
), attnum
, targetcollid
);
13255 * Drop any pg_statistic entry for the column, since it's now wrong type
13257 RemoveStatistics(RelationGetRelid(rel
), attnum
);
13259 InvokeObjectPostAlterHook(RelationRelationId
,
13260 RelationGetRelid(rel
), attnum
);
13263 * Update the default, if present, by brute force --- remove and re-add
13264 * the default. Probably unsafe to take shortcuts, since the new version
13265 * may well have additional dependencies. (It's okay to do this now,
13266 * rather than after other ALTER TYPE commands, since the default won't
13267 * depend on other column types.)
13272 * If it's a GENERATED default, drop its dependency records, in
13273 * particular its INTERNAL dependency on the column, which would
13274 * otherwise cause dependency.c to refuse to perform the deletion.
13276 if (attTup
->attgenerated
)
13278 Oid attrdefoid
= GetAttrDefaultOid(RelationGetRelid(rel
), attnum
);
13280 if (!OidIsValid(attrdefoid
))
13281 elog(ERROR
, "could not find attrdef tuple for relation %u attnum %d",
13282 RelationGetRelid(rel
), attnum
);
13283 (void) deleteDependencyRecordsFor(AttrDefaultRelationId
, attrdefoid
, false);
13287 * Make updates-so-far visible, particularly the new pg_attribute row
13288 * which will be updated again.
13290 CommandCounterIncrement();
13293 * We use RESTRICT here for safety, but at present we do not expect
13294 * anything to depend on the default.
13296 RemoveAttrDefault(RelationGetRelid(rel
), attnum
, DROP_RESTRICT
, true,
13299 StoreAttrDefault(rel
, attnum
, defaultexpr
, true, false);
13302 ObjectAddressSubSet(address
, RelationRelationId
,
13303 RelationGetRelid(rel
), attnum
);
13306 heap_freetuple(heapTup
);
13312 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13313 * that depends on the column (constraints, indexes, etc), and record enough
13314 * information to let us recreate the objects.
13317 RememberAllDependentForRebuilding(AlteredTableInfo
*tab
, AlterTableType subtype
,
13318 Relation rel
, AttrNumber attnum
, const char *colName
)
13321 ScanKeyData key
[3];
13325 Assert(subtype
== AT_AlterColumnType
|| subtype
== AT_SetExpression
);
13327 depRel
= table_open(DependRelationId
, RowExclusiveLock
);
13329 ScanKeyInit(&key
[0],
13330 Anum_pg_depend_refclassid
,
13331 BTEqualStrategyNumber
, F_OIDEQ
,
13332 ObjectIdGetDatum(RelationRelationId
));
13333 ScanKeyInit(&key
[1],
13334 Anum_pg_depend_refobjid
,
13335 BTEqualStrategyNumber
, F_OIDEQ
,
13336 ObjectIdGetDatum(RelationGetRelid(rel
)));
13337 ScanKeyInit(&key
[2],
13338 Anum_pg_depend_refobjsubid
,
13339 BTEqualStrategyNumber
, F_INT4EQ
,
13340 Int32GetDatum((int32
) attnum
));
13342 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
13345 while (HeapTupleIsValid(depTup
= systable_getnext(scan
)))
13347 Form_pg_depend foundDep
= (Form_pg_depend
) GETSTRUCT(depTup
);
13348 ObjectAddress foundObject
;
13350 foundObject
.classId
= foundDep
->classid
;
13351 foundObject
.objectId
= foundDep
->objid
;
13352 foundObject
.objectSubId
= foundDep
->objsubid
;
13354 switch (foundObject
.classId
)
13356 case RelationRelationId
:
13358 char relKind
= get_rel_relkind(foundObject
.objectId
);
13360 if (relKind
== RELKIND_INDEX
||
13361 relKind
== RELKIND_PARTITIONED_INDEX
)
13363 Assert(foundObject
.objectSubId
== 0);
13364 RememberIndexForRebuilding(foundObject
.objectId
, tab
);
13366 else if (relKind
== RELKIND_SEQUENCE
)
13369 * This must be a SERIAL column's sequence. We need
13370 * not do anything to it.
13372 Assert(foundObject
.objectSubId
== 0);
13376 /* Not expecting any other direct dependencies... */
13377 elog(ERROR
, "unexpected object depending on column: %s",
13378 getObjectDescription(&foundObject
, false));
13383 case ConstraintRelationId
:
13384 Assert(foundObject
.objectSubId
== 0);
13385 RememberConstraintForRebuilding(foundObject
.objectId
, tab
);
13388 case ProcedureRelationId
:
13391 * A new-style SQL function can depend on a column, if that
13392 * column is referenced in the parsed function body. Ideally
13393 * we'd automatically update the function by deparsing and
13394 * reparsing it, but that's risky and might well fail anyhow.
13397 * This is only a problem for AT_AlterColumnType, not
13398 * AT_SetExpression.
13400 if (subtype
== AT_AlterColumnType
)
13402 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13403 errmsg("cannot alter type of a column used by a function or procedure"),
13404 errdetail("%s depends on column \"%s\"",
13405 getObjectDescription(&foundObject
, false),
13409 case RewriteRelationId
:
13412 * View/rule bodies have pretty much the same issues as
13413 * function bodies. FIXME someday.
13415 if (subtype
== AT_AlterColumnType
)
13417 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13418 errmsg("cannot alter type of a column used by a view or rule"),
13419 errdetail("%s depends on column \"%s\"",
13420 getObjectDescription(&foundObject
, false),
13424 case TriggerRelationId
:
13427 * A trigger can depend on a column because the column is
13428 * specified as an update target, or because the column is
13429 * used in the trigger's WHEN condition. The first case would
13430 * not require any extra work, but the second case would
13431 * require updating the WHEN expression, which has the same
13432 * issues as above. Since we can't easily tell which case
13433 * applies, we punt for both. FIXME someday.
13435 if (subtype
== AT_AlterColumnType
)
13437 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13438 errmsg("cannot alter type of a column used in a trigger definition"),
13439 errdetail("%s depends on column \"%s\"",
13440 getObjectDescription(&foundObject
, false),
13444 case PolicyRelationId
:
13447 * A policy can depend on a column because the column is
13448 * specified in the policy's USING or WITH CHECK qual
13449 * expressions. It might be possible to rewrite and recheck
13450 * the policy expression, but punt for now. It's certainly
13451 * easy enough to remove and recreate the policy; still, FIXME
13454 if (subtype
== AT_AlterColumnType
)
13456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13457 errmsg("cannot alter type of a column used in a policy definition"),
13458 errdetail("%s depends on column \"%s\"",
13459 getObjectDescription(&foundObject
, false),
13463 case AttrDefaultRelationId
:
13465 ObjectAddress col
= GetAttrDefaultColumnAddress(foundObject
.objectId
);
13467 if (col
.objectId
== RelationGetRelid(rel
) &&
13468 col
.objectSubId
== attnum
)
13471 * Ignore the column's own default expression. The
13472 * caller deals with it.
13478 * This must be a reference from the expression of a
13479 * generated column elsewhere in the same table.
13480 * Changing the type/generated expression of a column
13481 * that is used by a generated column is not allowed
13482 * by SQL standard, so just punt for now. It might be
13483 * doable with some thinking and effort.
13485 if (subtype
== AT_AlterColumnType
)
13487 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13488 errmsg("cannot alter type of a column used by a generated column"),
13489 errdetail("Column \"%s\" is used by generated column \"%s\".",
13491 get_attname(col
.objectId
,
13498 case StatisticExtRelationId
:
13501 * Give the extended-stats machinery a chance to fix anything
13502 * that this column type change would break.
13504 RememberStatisticsForRebuilding(foundObject
.objectId
, tab
);
13507 case PublicationRelRelationId
:
13510 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
13511 * clause. Same issues as above. FIXME someday.
13513 if (subtype
== AT_AlterColumnType
)
13515 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
13516 errmsg("cannot alter type of a column used by a publication WHERE clause"),
13517 errdetail("%s depends on column \"%s\"",
13518 getObjectDescription(&foundObject
, false),
13525 * We don't expect any other sorts of objects to depend on a
13528 elog(ERROR
, "unexpected object depending on column: %s",
13529 getObjectDescription(&foundObject
, false));
13534 systable_endscan(scan
);
13535 table_close(depRel
, NoLock
);
13539 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13540 * needs to be reset.
13543 RememberReplicaIdentityForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13545 if (!get_index_isreplident(indoid
))
13548 if (tab
->replicaIdentityIndex
)
13549 elog(ERROR
, "relation %u has multiple indexes marked as replica identity", tab
->relid
);
13551 tab
->replicaIdentityIndex
= get_rel_name(indoid
);
13555 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13558 RememberClusterOnForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13560 if (!get_index_isclustered(indoid
))
13563 if (tab
->clusterOnIndex
)
13564 elog(ERROR
, "relation %u has multiple clustered indexes", tab
->relid
);
13566 tab
->clusterOnIndex
= get_rel_name(indoid
);
13570 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13571 * to be rebuilt (which we might already know).
13574 RememberConstraintForRebuilding(Oid conoid
, AlteredTableInfo
*tab
)
13577 * This de-duplication check is critical for two independent reasons: we
13578 * mustn't try to recreate the same constraint twice, and if a constraint
13579 * depends on more than one column whose type is to be altered, we must
13580 * capture its definition string before applying any of the column type
13581 * changes. ruleutils.c will get confused if we ask again later.
13583 if (!list_member_oid(tab
->changedConstraintOids
, conoid
))
13585 /* OK, capture the constraint's existing definition string */
13586 char *defstring
= pg_get_constraintdef_command(conoid
);
13589 tab
->changedConstraintOids
= lappend_oid(tab
->changedConstraintOids
,
13591 tab
->changedConstraintDefs
= lappend(tab
->changedConstraintDefs
,
13595 * For the index of a constraint, if any, remember if it is used for
13596 * the table's replica identity or if it is a clustered index, so that
13597 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13598 * those properties.
13600 indoid
= get_constraint_index(conoid
);
13601 if (OidIsValid(indoid
))
13603 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13604 RememberClusterOnForRebuilding(indoid
, tab
);
13610 * Subroutine for ATExecAlterColumnType: remember that an index needs
13611 * to be rebuilt (which we might already know).
13614 RememberIndexForRebuilding(Oid indoid
, AlteredTableInfo
*tab
)
13617 * This de-duplication check is critical for two independent reasons: we
13618 * mustn't try to recreate the same index twice, and if an index depends
13619 * on more than one column whose type is to be altered, we must capture
13620 * its definition string before applying any of the column type changes.
13621 * ruleutils.c will get confused if we ask again later.
13623 if (!list_member_oid(tab
->changedIndexOids
, indoid
))
13626 * Before adding it as an index-to-rebuild, we'd better see if it
13627 * belongs to a constraint, and if so rebuild the constraint instead.
13628 * Typically this check fails, because constraint indexes normally
13629 * have only dependencies on their constraint. But it's possible for
13630 * such an index to also have direct dependencies on table columns,
13631 * for example with a partial exclusion constraint.
13633 Oid conoid
= get_index_constraint(indoid
);
13635 if (OidIsValid(conoid
))
13637 RememberConstraintForRebuilding(conoid
, tab
);
13641 /* OK, capture the index's existing definition string */
13642 char *defstring
= pg_get_indexdef_string(indoid
);
13644 tab
->changedIndexOids
= lappend_oid(tab
->changedIndexOids
,
13646 tab
->changedIndexDefs
= lappend(tab
->changedIndexDefs
,
13650 * Remember if this index is used for the table's replica identity
13651 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13652 * can queue up commands necessary to restore those properties.
13654 RememberReplicaIdentityForRebuilding(indoid
, tab
);
13655 RememberClusterOnForRebuilding(indoid
, tab
);
13661 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13662 * needs to be rebuilt (which we might already know).
13665 RememberStatisticsForRebuilding(Oid stxoid
, AlteredTableInfo
*tab
)
13668 * This de-duplication check is critical for two independent reasons: we
13669 * mustn't try to recreate the same statistics object twice, and if the
13670 * statistics object depends on more than one column whose type is to be
13671 * altered, we must capture its definition string before applying any of
13672 * the type changes. ruleutils.c will get confused if we ask again later.
13674 if (!list_member_oid(tab
->changedStatisticsOids
, stxoid
))
13676 /* OK, capture the statistics object's existing definition string */
13677 char *defstring
= pg_get_statisticsobjdef_string(stxoid
);
13679 tab
->changedStatisticsOids
= lappend_oid(tab
->changedStatisticsOids
,
13681 tab
->changedStatisticsDefs
= lappend(tab
->changedStatisticsDefs
,
13687 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
13688 * operations for a particular relation. We have to drop and recreate all the
13689 * indexes and constraints that depend on the altered columns. We do the
13690 * actual dropping here, but re-creation is managed by adding work queue
13691 * entries to do those steps later.
13694 ATPostAlterTypeCleanup(List
**wqueue
, AlteredTableInfo
*tab
, LOCKMODE lockmode
)
13697 ObjectAddresses
*objects
;
13698 ListCell
*def_item
;
13699 ListCell
*oid_item
;
13702 * Collect all the constraints and indexes to drop so we can process them
13703 * in a single call. That way we don't have to worry about dependencies
13706 objects
= new_object_addresses();
13709 * Re-parse the index and constraint definitions, and attach them to the
13710 * appropriate work queue entries. We do this before dropping because in
13711 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13712 * lock on the table the constraint is attached to, and we need to get
13713 * that before reparsing/dropping.
13715 * We can't rely on the output of deparsing to tell us which relation to
13716 * operate on, because concurrent activity might have made the name
13717 * resolve differently. Instead, we've got to use the OID of the
13718 * constraint or index we're processing to figure out which relation to
13721 forboth(oid_item
, tab
->changedConstraintOids
,
13722 def_item
, tab
->changedConstraintDefs
)
13724 Oid oldId
= lfirst_oid(oid_item
);
13726 Form_pg_constraint con
;
13732 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
13733 if (!HeapTupleIsValid(tup
)) /* should not happen */
13734 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
13735 con
= (Form_pg_constraint
) GETSTRUCT(tup
);
13736 if (OidIsValid(con
->conrelid
))
13737 relid
= con
->conrelid
;
13740 /* must be a domain constraint */
13741 relid
= get_typ_typrelid(getBaseType(con
->contypid
));
13742 if (!OidIsValid(relid
))
13743 elog(ERROR
, "could not identify relation associated with constraint %u", oldId
);
13745 confrelid
= con
->confrelid
;
13746 contype
= con
->contype
;
13747 conislocal
= con
->conislocal
;
13748 ReleaseSysCache(tup
);
13750 ObjectAddressSet(obj
, ConstraintRelationId
, oldId
);
13751 add_exact_object_address(&obj
, objects
);
13754 * If the constraint is inherited (only), we don't want to inject a
13755 * new definition here; it'll get recreated when ATAddCheckConstraint
13756 * recurses from adding the parent table's constraint. But we had to
13757 * carry the info this far so that we can drop the constraint below.
13763 * When rebuilding an FK constraint that references the table we're
13764 * modifying, we might not yet have any lock on the FK's table, so get
13765 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13766 * step, so there's no value in asking for anything weaker.
13768 if (relid
!= tab
->relid
&& contype
== CONSTRAINT_FOREIGN
)
13769 LockRelationOid(relid
, AccessExclusiveLock
);
13771 ATPostAlterTypeParse(oldId
, relid
, confrelid
,
13772 (char *) lfirst(def_item
),
13773 wqueue
, lockmode
, tab
->rewrite
);
13775 forboth(oid_item
, tab
->changedIndexOids
,
13776 def_item
, tab
->changedIndexDefs
)
13778 Oid oldId
= lfirst_oid(oid_item
);
13781 relid
= IndexGetRelation(oldId
, false);
13782 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13783 (char *) lfirst(def_item
),
13784 wqueue
, lockmode
, tab
->rewrite
);
13786 ObjectAddressSet(obj
, RelationRelationId
, oldId
);
13787 add_exact_object_address(&obj
, objects
);
13790 /* add dependencies for new statistics */
13791 forboth(oid_item
, tab
->changedStatisticsOids
,
13792 def_item
, tab
->changedStatisticsDefs
)
13794 Oid oldId
= lfirst_oid(oid_item
);
13797 relid
= StatisticsGetRelation(oldId
, false);
13798 ATPostAlterTypeParse(oldId
, relid
, InvalidOid
,
13799 (char *) lfirst(def_item
),
13800 wqueue
, lockmode
, tab
->rewrite
);
13802 ObjectAddressSet(obj
, StatisticExtRelationId
, oldId
);
13803 add_exact_object_address(&obj
, objects
);
13807 * Queue up command to restore replica identity index marking
13809 if (tab
->replicaIdentityIndex
)
13811 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13812 ReplicaIdentityStmt
*subcmd
= makeNode(ReplicaIdentityStmt
);
13814 subcmd
->identity_type
= REPLICA_IDENTITY_INDEX
;
13815 subcmd
->name
= tab
->replicaIdentityIndex
;
13816 cmd
->subtype
= AT_ReplicaIdentity
;
13817 cmd
->def
= (Node
*) subcmd
;
13819 /* do it after indexes and constraints */
13820 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13821 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13825 * Queue up command to restore marking of index used for cluster.
13827 if (tab
->clusterOnIndex
)
13829 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
13831 cmd
->subtype
= AT_ClusterOn
;
13832 cmd
->name
= tab
->clusterOnIndex
;
13834 /* do it after indexes and constraints */
13835 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13836 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13840 * It should be okay to use DROP_RESTRICT here, since nothing else should
13841 * be depending on these objects.
13843 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
13845 free_object_addresses(objects
);
13848 * The objects will get recreated during subsequent passes over the work
13854 * Parse the previously-saved definition string for a constraint, index or
13855 * statistics object against the newly-established column data type(s), and
13856 * queue up the resulting command parsetrees for execution.
13858 * This might fail if, for example, you have a WHERE clause that uses an
13859 * operator that's not available for the new column type.
13862 ATPostAlterTypeParse(Oid oldId
, Oid oldRelId
, Oid refRelId
, char *cmd
,
13863 List
**wqueue
, LOCKMODE lockmode
, bool rewrite
)
13865 List
*raw_parsetree_list
;
13866 List
*querytree_list
;
13867 ListCell
*list_item
;
13871 * We expect that we will get only ALTER TABLE and CREATE INDEX
13872 * statements. Hence, there is no need to pass them through
13873 * parse_analyze_*() or the rewriter, but instead we need to pass them
13874 * through parse_utilcmd.c to make them ready for execution.
13876 raw_parsetree_list
= raw_parser(cmd
, RAW_PARSE_DEFAULT
);
13877 querytree_list
= NIL
;
13878 foreach(list_item
, raw_parsetree_list
)
13880 RawStmt
*rs
= lfirst_node(RawStmt
, list_item
);
13881 Node
*stmt
= rs
->stmt
;
13883 if (IsA(stmt
, IndexStmt
))
13884 querytree_list
= lappend(querytree_list
,
13885 transformIndexStmt(oldRelId
,
13886 (IndexStmt
*) stmt
,
13888 else if (IsA(stmt
, AlterTableStmt
))
13893 stmt
= (Node
*) transformAlterTableStmt(oldRelId
,
13894 (AlterTableStmt
*) stmt
,
13898 querytree_list
= list_concat(querytree_list
, beforeStmts
);
13899 querytree_list
= lappend(querytree_list
, stmt
);
13900 querytree_list
= list_concat(querytree_list
, afterStmts
);
13902 else if (IsA(stmt
, CreateStatsStmt
))
13903 querytree_list
= lappend(querytree_list
,
13904 transformStatsStmt(oldRelId
,
13905 (CreateStatsStmt
*) stmt
,
13908 querytree_list
= lappend(querytree_list
, stmt
);
13911 /* Caller should already have acquired whatever lock we need. */
13912 rel
= relation_open(oldRelId
, NoLock
);
13915 * Attach each generated command to the proper place in the work queue.
13916 * Note this could result in creation of entirely new work-queue entries.
13918 * Also note that we have to tweak the command subtypes, because it turns
13919 * out that re-creation of indexes and constraints has to act a bit
13920 * differently from initial creation.
13922 foreach(list_item
, querytree_list
)
13924 Node
*stm
= (Node
*) lfirst(list_item
);
13925 AlteredTableInfo
*tab
;
13927 tab
= ATGetQueueEntry(wqueue
, rel
);
13929 if (IsA(stm
, IndexStmt
))
13931 IndexStmt
*stmt
= (IndexStmt
*) stm
;
13932 AlterTableCmd
*newcmd
;
13935 TryReuseIndex(oldId
, stmt
);
13936 stmt
->reset_default_tblspc
= true;
13937 /* keep the index's comment */
13938 stmt
->idxcomment
= GetComment(oldId
, RelationRelationId
, 0);
13940 newcmd
= makeNode(AlterTableCmd
);
13941 newcmd
->subtype
= AT_ReAddIndex
;
13942 newcmd
->def
= (Node
*) stmt
;
13943 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
13944 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], newcmd
);
13946 else if (IsA(stm
, AlterTableStmt
))
13948 AlterTableStmt
*stmt
= (AlterTableStmt
*) stm
;
13951 foreach(lcmd
, stmt
->cmds
)
13953 AlterTableCmd
*cmd
= lfirst_node(AlterTableCmd
, lcmd
);
13955 if (cmd
->subtype
== AT_AddIndex
)
13957 IndexStmt
*indstmt
;
13960 indstmt
= castNode(IndexStmt
, cmd
->def
);
13961 indoid
= get_constraint_index(oldId
);
13964 TryReuseIndex(indoid
, indstmt
);
13965 /* keep any comment on the index */
13966 indstmt
->idxcomment
= GetComment(indoid
,
13967 RelationRelationId
, 0);
13968 indstmt
->reset_default_tblspc
= true;
13970 cmd
->subtype
= AT_ReAddIndex
;
13971 tab
->subcmds
[AT_PASS_OLD_INDEX
] =
13972 lappend(tab
->subcmds
[AT_PASS_OLD_INDEX
], cmd
);
13974 /* recreate any comment on the constraint */
13975 RebuildConstraintComment(tab
,
13982 else if (cmd
->subtype
== AT_AddConstraint
)
13984 Constraint
*con
= castNode(Constraint
, cmd
->def
);
13986 con
->old_pktable_oid
= refRelId
;
13987 /* rewriting neither side of a FK */
13988 if (con
->contype
== CONSTR_FOREIGN
&&
13989 !rewrite
&& tab
->rewrite
== 0)
13990 TryReuseForeignKey(oldId
, con
);
13991 con
->reset_default_tblspc
= true;
13992 cmd
->subtype
= AT_ReAddConstraint
;
13993 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
13994 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
13996 /* recreate any comment on the constraint */
13997 RebuildConstraintComment(tab
,
13998 AT_PASS_OLD_CONSTR
,
14004 else if (cmd
->subtype
== AT_SetNotNull
)
14007 * The parser will create AT_SetNotNull subcommands for
14008 * columns of PRIMARY KEY indexes/constraints, but we need
14009 * not do anything with them here, because the columns'
14010 * NOT NULL marks will already have been propagated into
14011 * the new table definition.
14015 elog(ERROR
, "unexpected statement subtype: %d",
14016 (int) cmd
->subtype
);
14019 else if (IsA(stm
, AlterDomainStmt
))
14021 AlterDomainStmt
*stmt
= (AlterDomainStmt
*) stm
;
14023 if (stmt
->subtype
== 'C') /* ADD CONSTRAINT */
14025 Constraint
*con
= castNode(Constraint
, stmt
->def
);
14026 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
14028 cmd
->subtype
= AT_ReAddDomainConstraint
;
14029 cmd
->def
= (Node
*) stmt
;
14030 tab
->subcmds
[AT_PASS_OLD_CONSTR
] =
14031 lappend(tab
->subcmds
[AT_PASS_OLD_CONSTR
], cmd
);
14033 /* recreate any comment on the constraint */
14034 RebuildConstraintComment(tab
,
14035 AT_PASS_OLD_CONSTR
,
14042 elog(ERROR
, "unexpected statement subtype: %d",
14043 (int) stmt
->subtype
);
14045 else if (IsA(stm
, CreateStatsStmt
))
14047 CreateStatsStmt
*stmt
= (CreateStatsStmt
*) stm
;
14048 AlterTableCmd
*newcmd
;
14050 /* keep the statistics object's comment */
14051 stmt
->stxcomment
= GetComment(oldId
, StatisticExtRelationId
, 0);
14053 newcmd
= makeNode(AlterTableCmd
);
14054 newcmd
->subtype
= AT_ReAddStatistics
;
14055 newcmd
->def
= (Node
*) stmt
;
14056 tab
->subcmds
[AT_PASS_MISC
] =
14057 lappend(tab
->subcmds
[AT_PASS_MISC
], newcmd
);
14060 elog(ERROR
, "unexpected statement type: %d",
14061 (int) nodeTag(stm
));
14064 relation_close(rel
, NoLock
);
14068 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14069 * for a table or domain constraint that is being rebuilt.
14071 * objid is the OID of the constraint.
14072 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14073 * as a string list) for a domain constraint.
14074 * (We could dig that info, as well as the conname, out of the pg_constraint
14075 * entry; but callers already have them so might as well pass them.)
14078 RebuildConstraintComment(AlteredTableInfo
*tab
, AlterTablePass pass
, Oid objid
,
14079 Relation rel
, List
*domname
,
14080 const char *conname
)
14084 AlterTableCmd
*newcmd
;
14086 /* Look for comment for object wanted, and leave if none */
14087 comment_str
= GetComment(objid
, ConstraintRelationId
, 0);
14088 if (comment_str
== NULL
)
14091 /* Build CommentStmt node, copying all input data for safety */
14092 cmd
= makeNode(CommentStmt
);
14095 cmd
->objtype
= OBJECT_TABCONSTRAINT
;
14096 cmd
->object
= (Node
*)
14097 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel
))),
14098 makeString(pstrdup(RelationGetRelationName(rel
))),
14099 makeString(pstrdup(conname
)));
14103 cmd
->objtype
= OBJECT_DOMCONSTRAINT
;
14104 cmd
->object
= (Node
*)
14105 list_make2(makeTypeNameFromNameList(copyObject(domname
)),
14106 makeString(pstrdup(conname
)));
14108 cmd
->comment
= comment_str
;
14110 /* Append it to list of commands */
14111 newcmd
= makeNode(AlterTableCmd
);
14112 newcmd
->subtype
= AT_ReAddComment
;
14113 newcmd
->def
= (Node
*) cmd
;
14114 tab
->subcmds
[pass
] = lappend(tab
->subcmds
[pass
], newcmd
);
14118 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14119 * for the real analysis, then mutates the IndexStmt based on that verdict.
14122 TryReuseIndex(Oid oldId
, IndexStmt
*stmt
)
14124 if (CheckIndexCompatible(oldId
,
14125 stmt
->accessMethod
,
14127 stmt
->excludeOpNames
))
14129 Relation irel
= index_open(oldId
, NoLock
);
14131 /* If it's a partitioned index, there is no storage to share. */
14132 if (irel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
14134 stmt
->oldNumber
= irel
->rd_locator
.relNumber
;
14135 stmt
->oldCreateSubid
= irel
->rd_createSubid
;
14136 stmt
->oldFirstRelfilelocatorSubid
= irel
->rd_firstRelfilelocatorSubid
;
14138 index_close(irel
, NoLock
);
14143 * Subroutine for ATPostAlterTypeParse().
14145 * Stash the old P-F equality operator into the Constraint node, for possible
14146 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14147 * this constraint can be skipped.
14150 TryReuseForeignKey(Oid oldId
, Constraint
*con
)
14159 Assert(con
->contype
== CONSTR_FOREIGN
);
14160 Assert(con
->old_conpfeqop
== NIL
); /* already prepared this node */
14162 tup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(oldId
));
14163 if (!HeapTupleIsValid(tup
)) /* should not happen */
14164 elog(ERROR
, "cache lookup failed for constraint %u", oldId
);
14166 adatum
= SysCacheGetAttrNotNull(CONSTROID
, tup
,
14167 Anum_pg_constraint_conpfeqop
);
14168 arr
= DatumGetArrayTypeP(adatum
); /* ensure not toasted */
14169 numkeys
= ARR_DIMS(arr
)[0];
14170 /* test follows the one in ri_FetchConstraintInfo() */
14171 if (ARR_NDIM(arr
) != 1 ||
14172 ARR_HASNULL(arr
) ||
14173 ARR_ELEMTYPE(arr
) != OIDOID
)
14174 elog(ERROR
, "conpfeqop is not a 1-D Oid array");
14175 rawarr
= (Oid
*) ARR_DATA_PTR(arr
);
14177 /* stash a List of the operator Oids in our Constraint node */
14178 for (i
= 0; i
< numkeys
; i
++)
14179 con
->old_conpfeqop
= lappend_oid(con
->old_conpfeqop
, rawarr
[i
]);
14181 ReleaseSysCache(tup
);
14185 * ALTER COLUMN .. OPTIONS ( ... )
14187 * Returns the address of the modified column
14189 static ObjectAddress
14190 ATExecAlterColumnGenericOptions(Relation rel
,
14191 const char *colName
,
14197 ForeignServer
*server
;
14198 ForeignDataWrapper
*fdw
;
14200 HeapTuple newtuple
;
14202 Datum repl_val
[Natts_pg_attribute
];
14203 bool repl_null
[Natts_pg_attribute
];
14204 bool repl_repl
[Natts_pg_attribute
];
14206 Form_pg_foreign_table fttableform
;
14207 Form_pg_attribute atttableform
;
14209 ObjectAddress address
;
14211 if (options
== NIL
)
14212 return InvalidObjectAddress
;
14214 /* First, determine FDW validator associated to the foreign table. */
14215 ftrel
= table_open(ForeignTableRelationId
, AccessShareLock
);
14216 tuple
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(rel
->rd_id
));
14217 if (!HeapTupleIsValid(tuple
))
14219 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14220 errmsg("foreign table \"%s\" does not exist",
14221 RelationGetRelationName(rel
))));
14222 fttableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
14223 server
= GetForeignServer(fttableform
->ftserver
);
14224 fdw
= GetForeignDataWrapper(server
->fdwid
);
14226 table_close(ftrel
, AccessShareLock
);
14227 ReleaseSysCache(tuple
);
14229 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
14230 tuple
= SearchSysCacheAttName(RelationGetRelid(rel
), colName
);
14231 if (!HeapTupleIsValid(tuple
))
14233 (errcode(ERRCODE_UNDEFINED_COLUMN
),
14234 errmsg("column \"%s\" of relation \"%s\" does not exist",
14235 colName
, RelationGetRelationName(rel
))));
14237 /* Prevent them from altering a system attribute */
14238 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
14239 attnum
= atttableform
->attnum
;
14242 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14243 errmsg("cannot alter system column \"%s\"", colName
)));
14246 /* Initialize buffers for new tuple values */
14247 memset(repl_val
, 0, sizeof(repl_val
));
14248 memset(repl_null
, false, sizeof(repl_null
));
14249 memset(repl_repl
, false, sizeof(repl_repl
));
14251 /* Extract the current options */
14252 datum
= SysCacheGetAttr(ATTNAME
,
14254 Anum_pg_attribute_attfdwoptions
,
14257 datum
= PointerGetDatum(NULL
);
14259 /* Transform the options */
14260 datum
= transformGenericOptions(AttributeRelationId
,
14263 fdw
->fdwvalidator
);
14265 if (PointerIsValid(DatumGetPointer(datum
)))
14266 repl_val
[Anum_pg_attribute_attfdwoptions
- 1] = datum
;
14268 repl_null
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14270 repl_repl
[Anum_pg_attribute_attfdwoptions
- 1] = true;
14272 /* Everything looks good - update the tuple */
14274 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(attrel
),
14275 repl_val
, repl_null
, repl_repl
);
14277 CatalogTupleUpdate(attrel
, &newtuple
->t_self
, newtuple
);
14279 InvokeObjectPostAlterHook(RelationRelationId
,
14280 RelationGetRelid(rel
),
14281 atttableform
->attnum
);
14282 ObjectAddressSubSet(address
, RelationRelationId
,
14283 RelationGetRelid(rel
), attnum
);
14285 ReleaseSysCache(tuple
);
14287 table_close(attrel
, RowExclusiveLock
);
14289 heap_freetuple(newtuple
);
14295 * ALTER TABLE OWNER
14297 * recursing is true if we are recursing from a table to its indexes,
14298 * sequences, or toast table. We don't allow the ownership of those things to
14299 * be changed separately from the parent table. Also, we can skip permission
14300 * checks (this is necessary not just an optimization, else we'd fail to
14301 * handle toast tables properly).
14303 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14304 * free-standing composite type.
14307 ATExecChangeOwner(Oid relationOid
, Oid newOwnerId
, bool recursing
, LOCKMODE lockmode
)
14309 Relation target_rel
;
14310 Relation class_rel
;
14312 Form_pg_class tuple_class
;
14315 * Get exclusive lock till end of transaction on the target table. Use
14316 * relation_open so that we can work on indexes and sequences.
14318 target_rel
= relation_open(relationOid
, lockmode
);
14320 /* Get its pg_class tuple, too */
14321 class_rel
= table_open(RelationRelationId
, RowExclusiveLock
);
14323 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relationOid
));
14324 if (!HeapTupleIsValid(tuple
))
14325 elog(ERROR
, "cache lookup failed for relation %u", relationOid
);
14326 tuple_class
= (Form_pg_class
) GETSTRUCT(tuple
);
14328 /* Can we change the ownership of this tuple? */
14329 switch (tuple_class
->relkind
)
14331 case RELKIND_RELATION
:
14333 case RELKIND_MATVIEW
:
14334 case RELKIND_FOREIGN_TABLE
:
14335 case RELKIND_PARTITIONED_TABLE
:
14336 /* ok to change owner */
14338 case RELKIND_INDEX
:
14342 * Because ALTER INDEX OWNER used to be allowed, and in fact
14343 * is generated by old versions of pg_dump, we give a warning
14344 * and do nothing rather than erroring out. Also, to avoid
14345 * unnecessary chatter while restoring those old dumps, say
14346 * nothing at all if the command would be a no-op anyway.
14348 if (tuple_class
->relowner
!= newOwnerId
)
14350 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14351 errmsg("cannot change owner of index \"%s\"",
14352 NameStr(tuple_class
->relname
)),
14353 errhint("Change the ownership of the index's table instead.")));
14354 /* quick hack to exit via the no-op path */
14355 newOwnerId
= tuple_class
->relowner
;
14358 case RELKIND_PARTITIONED_INDEX
:
14362 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14363 errmsg("cannot change owner of index \"%s\"",
14364 NameStr(tuple_class
->relname
)),
14365 errhint("Change the ownership of the index's table instead.")));
14367 case RELKIND_SEQUENCE
:
14369 tuple_class
->relowner
!= newOwnerId
)
14371 /* if it's an owned sequence, disallow changing it by itself */
14375 if (sequenceIsOwned(relationOid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
14376 sequenceIsOwned(relationOid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
14378 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14379 errmsg("cannot change owner of sequence \"%s\"",
14380 NameStr(tuple_class
->relname
)),
14381 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14382 NameStr(tuple_class
->relname
),
14383 get_rel_name(tableId
))));
14386 case RELKIND_COMPOSITE_TYPE
:
14390 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14391 errmsg("\"%s\" is a composite type",
14392 NameStr(tuple_class
->relname
)),
14393 /* translator: %s is an SQL ALTER command */
14394 errhint("Use %s instead.",
14397 case RELKIND_TOASTVALUE
:
14403 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14404 errmsg("cannot change owner of relation \"%s\"",
14405 NameStr(tuple_class
->relname
)),
14406 errdetail_relkind_not_supported(tuple_class
->relkind
)));
14410 * If the new owner is the same as the existing owner, consider the
14411 * command to have succeeded. This is for dump restoration purposes.
14413 if (tuple_class
->relowner
!= newOwnerId
)
14415 Datum repl_val
[Natts_pg_class
];
14416 bool repl_null
[Natts_pg_class
];
14417 bool repl_repl
[Natts_pg_class
];
14421 HeapTuple newtuple
;
14423 /* skip permission checks when recursing to index or toast table */
14426 /* Superusers can always do it */
14429 Oid namespaceOid
= tuple_class
->relnamespace
;
14430 AclResult aclresult
;
14432 /* Otherwise, must be owner of the existing object */
14433 if (!object_ownercheck(RelationRelationId
, relationOid
, GetUserId()))
14434 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relationOid
)),
14435 RelationGetRelationName(target_rel
));
14437 /* Must be able to become new owner */
14438 check_can_set_role(GetUserId(), newOwnerId
);
14440 /* New owner must have CREATE privilege on namespace */
14441 aclresult
= object_aclcheck(NamespaceRelationId
, namespaceOid
, newOwnerId
,
14443 if (aclresult
!= ACLCHECK_OK
)
14444 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
14445 get_namespace_name(namespaceOid
));
14449 memset(repl_null
, false, sizeof(repl_null
));
14450 memset(repl_repl
, false, sizeof(repl_repl
));
14452 repl_repl
[Anum_pg_class_relowner
- 1] = true;
14453 repl_val
[Anum_pg_class_relowner
- 1] = ObjectIdGetDatum(newOwnerId
);
14456 * Determine the modified ACL for the new owner. This is only
14457 * necessary when the ACL is non-null.
14459 aclDatum
= SysCacheGetAttr(RELOID
, tuple
,
14460 Anum_pg_class_relacl
,
14464 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
14465 tuple_class
->relowner
, newOwnerId
);
14466 repl_repl
[Anum_pg_class_relacl
- 1] = true;
14467 repl_val
[Anum_pg_class_relacl
- 1] = PointerGetDatum(newAcl
);
14470 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(class_rel
), repl_val
, repl_null
, repl_repl
);
14472 CatalogTupleUpdate(class_rel
, &newtuple
->t_self
, newtuple
);
14474 heap_freetuple(newtuple
);
14477 * We must similarly update any per-column ACLs to reflect the new
14478 * owner; for neatness reasons that's split out as a subroutine.
14480 change_owner_fix_column_acls(relationOid
,
14481 tuple_class
->relowner
,
14485 * Update owner dependency reference, if any. A composite type has
14486 * none, because it's tracked for the pg_type entry instead of here;
14487 * indexes and TOAST tables don't have their own entries either.
14489 if (tuple_class
->relkind
!= RELKIND_COMPOSITE_TYPE
&&
14490 tuple_class
->relkind
!= RELKIND_INDEX
&&
14491 tuple_class
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
14492 tuple_class
->relkind
!= RELKIND_TOASTVALUE
)
14493 changeDependencyOnOwner(RelationRelationId
, relationOid
,
14497 * Also change the ownership of the table's row type, if it has one
14499 if (OidIsValid(tuple_class
->reltype
))
14500 AlterTypeOwnerInternal(tuple_class
->reltype
, newOwnerId
);
14503 * If we are operating on a table or materialized view, also change
14504 * the ownership of any indexes and sequences that belong to the
14505 * relation, as well as its toast table (if it has one).
14507 if (tuple_class
->relkind
== RELKIND_RELATION
||
14508 tuple_class
->relkind
== RELKIND_PARTITIONED_TABLE
||
14509 tuple_class
->relkind
== RELKIND_MATVIEW
||
14510 tuple_class
->relkind
== RELKIND_TOASTVALUE
)
14512 List
*index_oid_list
;
14515 /* Find all the indexes belonging to this relation */
14516 index_oid_list
= RelationGetIndexList(target_rel
);
14518 /* For each index, recursively change its ownership */
14519 foreach(i
, index_oid_list
)
14520 ATExecChangeOwner(lfirst_oid(i
), newOwnerId
, true, lockmode
);
14522 list_free(index_oid_list
);
14525 /* If it has a toast table, recurse to change its ownership */
14526 if (tuple_class
->reltoastrelid
!= InvalidOid
)
14527 ATExecChangeOwner(tuple_class
->reltoastrelid
, newOwnerId
,
14530 /* If it has dependent sequences, recurse to change them too */
14531 change_owner_recurse_to_sequences(relationOid
, newOwnerId
, lockmode
);
14534 InvokeObjectPostAlterHook(RelationRelationId
, relationOid
, 0);
14536 ReleaseSysCache(tuple
);
14537 table_close(class_rel
, RowExclusiveLock
);
14538 relation_close(target_rel
, NoLock
);
14542 * change_owner_fix_column_acls
14544 * Helper function for ATExecChangeOwner. Scan the columns of the table
14545 * and fix any non-null column ACLs to reflect the new owner.
14548 change_owner_fix_column_acls(Oid relationOid
, Oid oldOwnerId
, Oid newOwnerId
)
14550 Relation attRelation
;
14552 ScanKeyData key
[1];
14553 HeapTuple attributeTuple
;
14555 attRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
14556 ScanKeyInit(&key
[0],
14557 Anum_pg_attribute_attrelid
,
14558 BTEqualStrategyNumber
, F_OIDEQ
,
14559 ObjectIdGetDatum(relationOid
));
14560 scan
= systable_beginscan(attRelation
, AttributeRelidNumIndexId
,
14561 true, NULL
, 1, key
);
14562 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
14564 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
14565 Datum repl_val
[Natts_pg_attribute
];
14566 bool repl_null
[Natts_pg_attribute
];
14567 bool repl_repl
[Natts_pg_attribute
];
14571 HeapTuple newtuple
;
14573 /* Ignore dropped columns */
14574 if (att
->attisdropped
)
14577 aclDatum
= heap_getattr(attributeTuple
,
14578 Anum_pg_attribute_attacl
,
14579 RelationGetDescr(attRelation
),
14581 /* Null ACLs do not require changes */
14585 memset(repl_null
, false, sizeof(repl_null
));
14586 memset(repl_repl
, false, sizeof(repl_repl
));
14588 newAcl
= aclnewowner(DatumGetAclP(aclDatum
),
14589 oldOwnerId
, newOwnerId
);
14590 repl_repl
[Anum_pg_attribute_attacl
- 1] = true;
14591 repl_val
[Anum_pg_attribute_attacl
- 1] = PointerGetDatum(newAcl
);
14593 newtuple
= heap_modify_tuple(attributeTuple
,
14594 RelationGetDescr(attRelation
),
14595 repl_val
, repl_null
, repl_repl
);
14597 CatalogTupleUpdate(attRelation
, &newtuple
->t_self
, newtuple
);
14599 heap_freetuple(newtuple
);
14601 systable_endscan(scan
);
14602 table_close(attRelation
, RowExclusiveLock
);
14606 * change_owner_recurse_to_sequences
14608 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14609 * for sequences that are dependent on serial columns, and changes their
14613 change_owner_recurse_to_sequences(Oid relationOid
, Oid newOwnerId
, LOCKMODE lockmode
)
14617 ScanKeyData key
[2];
14621 * SERIAL sequences are those having an auto dependency on one of the
14622 * table's columns (we don't care *which* column, exactly).
14624 depRel
= table_open(DependRelationId
, AccessShareLock
);
14626 ScanKeyInit(&key
[0],
14627 Anum_pg_depend_refclassid
,
14628 BTEqualStrategyNumber
, F_OIDEQ
,
14629 ObjectIdGetDatum(RelationRelationId
));
14630 ScanKeyInit(&key
[1],
14631 Anum_pg_depend_refobjid
,
14632 BTEqualStrategyNumber
, F_OIDEQ
,
14633 ObjectIdGetDatum(relationOid
));
14634 /* we leave refobjsubid unspecified */
14636 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
14639 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
14641 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
14644 /* skip dependencies other than auto dependencies on columns */
14645 if (depForm
->refobjsubid
== 0 ||
14646 depForm
->classid
!= RelationRelationId
||
14647 depForm
->objsubid
!= 0 ||
14648 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
14651 /* Use relation_open just in case it's an index */
14652 seqRel
= relation_open(depForm
->objid
, lockmode
);
14654 /* skip non-sequence relations */
14655 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
14657 /* No need to keep the lock */
14658 relation_close(seqRel
, lockmode
);
14662 /* We don't need to close the sequence while we alter it. */
14663 ATExecChangeOwner(depForm
->objid
, newOwnerId
, true, lockmode
);
14665 /* Now we can close it. Keep the lock till end of transaction. */
14666 relation_close(seqRel
, NoLock
);
14669 systable_endscan(scan
);
14671 relation_close(depRel
, AccessShareLock
);
14675 * ALTER TABLE CLUSTER ON
14677 * The only thing we have to do is to change the indisclustered bits.
14679 * Return the address of the new clustering index.
14681 static ObjectAddress
14682 ATExecClusterOn(Relation rel
, const char *indexName
, LOCKMODE lockmode
)
14685 ObjectAddress address
;
14687 indexOid
= get_relname_relid(indexName
, rel
->rd_rel
->relnamespace
);
14689 if (!OidIsValid(indexOid
))
14691 (errcode(ERRCODE_UNDEFINED_OBJECT
),
14692 errmsg("index \"%s\" for table \"%s\" does not exist",
14693 indexName
, RelationGetRelationName(rel
))));
14695 /* Check index is valid to cluster on */
14696 check_index_is_clusterable(rel
, indexOid
, lockmode
);
14698 /* And do the work */
14699 mark_index_clustered(rel
, indexOid
, false);
14701 ObjectAddressSet(address
,
14702 RelationRelationId
, indexOid
);
14708 * ALTER TABLE SET WITHOUT CLUSTER
14710 * We have to find any indexes on the table that have indisclustered bit
14711 * set and turn it off.
14714 ATExecDropCluster(Relation rel
, LOCKMODE lockmode
)
14716 mark_index_clustered(rel
, InvalidOid
, false);
14720 * Preparation phase for SET ACCESS METHOD
14722 * Check that the access method exists and determine whether a change is
14726 ATPrepSetAccessMethod(AlteredTableInfo
*tab
, Relation rel
, const char *amname
)
14731 * Look up the access method name and check that it differs from the
14732 * table's current AM. If DEFAULT was specified for a partitioned table
14733 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
14735 if (amname
!= NULL
)
14736 amoid
= get_table_am_oid(amname
, false);
14737 else if (rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
14738 amoid
= InvalidOid
;
14740 amoid
= get_table_am_oid(default_table_access_method
, false);
14742 /* if it's a match, phase 3 doesn't need to do anything */
14743 if (rel
->rd_rel
->relam
== amoid
)
14746 /* Save info for Phase 3 to do the real work */
14747 tab
->rewrite
|= AT_REWRITE_ACCESS_METHOD
;
14748 tab
->newAccessMethod
= amoid
;
14749 tab
->chgAccessMethod
= true;
14753 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
14754 * storage that have an interest in preserving AM.
14756 * Since these have no storage, setting the access method is a catalog only
14760 ATExecSetAccessMethodNoStorage(Relation rel
, Oid newAccessMethodId
)
14763 Oid oldAccessMethodId
;
14765 Form_pg_class rd_rel
;
14766 Oid reloid
= RelationGetRelid(rel
);
14769 * Shouldn't be called on relations having storage; these are processed in
14772 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
14774 /* Get a modifiable copy of the relation's pg_class row. */
14775 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
14777 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(reloid
));
14778 if (!HeapTupleIsValid(tuple
))
14779 elog(ERROR
, "cache lookup failed for relation %u", reloid
);
14780 rd_rel
= (Form_pg_class
) GETSTRUCT(tuple
);
14782 /* Update the pg_class row. */
14783 oldAccessMethodId
= rd_rel
->relam
;
14784 rd_rel
->relam
= newAccessMethodId
;
14786 /* Leave if no update required */
14787 if (rd_rel
->relam
== oldAccessMethodId
)
14789 heap_freetuple(tuple
);
14790 table_close(pg_class
, RowExclusiveLock
);
14794 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
14797 * Update the dependency on the new access method. No dependency is added
14798 * if the new access method is InvalidOid (default case). Be very careful
14799 * that this has to compare the previous value stored in pg_class with the
14802 if (!OidIsValid(oldAccessMethodId
) && OidIsValid(rd_rel
->relam
))
14804 ObjectAddress relobj
,
14808 * New access method is defined and there was no dependency
14809 * previously, so record a new one.
14811 ObjectAddressSet(relobj
, RelationRelationId
, reloid
);
14812 ObjectAddressSet(referenced
, AccessMethodRelationId
, rd_rel
->relam
);
14813 recordDependencyOn(&relobj
, &referenced
, DEPENDENCY_NORMAL
);
14815 else if (OidIsValid(oldAccessMethodId
) &&
14816 !OidIsValid(rd_rel
->relam
))
14819 * There was an access method defined, and no new one, so just remove
14820 * the existing dependency.
14822 deleteDependencyRecordsForClass(RelationRelationId
, reloid
,
14823 AccessMethodRelationId
,
14824 DEPENDENCY_NORMAL
);
14828 Assert(OidIsValid(oldAccessMethodId
) &&
14829 OidIsValid(rd_rel
->relam
));
14831 /* Both are valid, so update the dependency */
14832 changeDependencyFor(RelationRelationId
, reloid
,
14833 AccessMethodRelationId
,
14834 oldAccessMethodId
, rd_rel
->relam
);
14837 /* make the relam and dependency changes visible */
14838 CommandCounterIncrement();
14840 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
14842 heap_freetuple(tuple
);
14843 table_close(pg_class
, RowExclusiveLock
);
14847 * ALTER TABLE SET TABLESPACE
14850 ATPrepSetTableSpace(AlteredTableInfo
*tab
, Relation rel
, const char *tablespacename
, LOCKMODE lockmode
)
14854 /* Check that the tablespace exists */
14855 tablespaceId
= get_tablespace_oid(tablespacename
, false);
14857 /* Check permissions except when moving to database's default */
14858 if (OidIsValid(tablespaceId
) && tablespaceId
!= MyDatabaseTableSpace
)
14860 AclResult aclresult
;
14862 aclresult
= object_aclcheck(TableSpaceRelationId
, tablespaceId
, GetUserId(), ACL_CREATE
);
14863 if (aclresult
!= ACLCHECK_OK
)
14864 aclcheck_error(aclresult
, OBJECT_TABLESPACE
, tablespacename
);
14867 /* Save info for Phase 3 to do the real work */
14868 if (OidIsValid(tab
->newTableSpace
))
14870 (errcode(ERRCODE_SYNTAX_ERROR
),
14871 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14873 tab
->newTableSpace
= tablespaceId
;
14877 * Set, reset, or replace reloptions.
14880 ATExecSetRelOptions(Relation rel
, List
*defList
, AlterTableType operation
,
14886 HeapTuple newtuple
;
14890 Datum repl_val
[Natts_pg_class
];
14891 bool repl_null
[Natts_pg_class
];
14892 bool repl_repl
[Natts_pg_class
];
14893 static char *validnsps
[] = HEAP_RELOPT_NAMESPACES
;
14895 if (defList
== NIL
&& operation
!= AT_ReplaceRelOptions
)
14896 return; /* nothing to do */
14898 pgclass
= table_open(RelationRelationId
, RowExclusiveLock
);
14900 /* Fetch heap tuple */
14901 relid
= RelationGetRelid(rel
);
14902 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
14903 if (!HeapTupleIsValid(tuple
))
14904 elog(ERROR
, "cache lookup failed for relation %u", relid
);
14906 if (operation
== AT_ReplaceRelOptions
)
14909 * If we're supposed to replace the reloptions list, we just pretend
14910 * there were none before.
14917 /* Get the old reloptions */
14918 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
14922 /* Generate new proposed reloptions (text array) */
14923 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
14924 defList
, NULL
, validnsps
, false,
14925 operation
== AT_ResetRelOptions
);
14928 switch (rel
->rd_rel
->relkind
)
14930 case RELKIND_RELATION
:
14931 case RELKIND_TOASTVALUE
:
14932 case RELKIND_MATVIEW
:
14933 (void) heap_reloptions(rel
->rd_rel
->relkind
, newOptions
, true);
14935 case RELKIND_PARTITIONED_TABLE
:
14936 (void) partitioned_table_reloptions(newOptions
, true);
14939 (void) view_reloptions(newOptions
, true);
14941 case RELKIND_INDEX
:
14942 case RELKIND_PARTITIONED_INDEX
:
14943 (void) index_reloptions(rel
->rd_indam
->amoptions
, newOptions
, true);
14947 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
14948 errmsg("cannot set options for relation \"%s\"",
14949 RelationGetRelationName(rel
)),
14950 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
14954 /* Special-case validation of view options */
14955 if (rel
->rd_rel
->relkind
== RELKIND_VIEW
)
14957 Query
*view_query
= get_view_query(rel
);
14958 List
*view_options
= untransformRelOptions(newOptions
);
14960 bool check_option
= false;
14962 foreach(cell
, view_options
)
14964 DefElem
*defel
= (DefElem
*) lfirst(cell
);
14966 if (strcmp(defel
->defname
, "check_option") == 0)
14967 check_option
= true;
14971 * If the check option is specified, look to see if the view is
14972 * actually auto-updatable or not.
14976 const char *view_updatable_error
=
14977 view_query_is_auto_updatable(view_query
, true);
14979 if (view_updatable_error
)
14981 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
14982 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14983 errhint("%s", _(view_updatable_error
))));
14988 * All we need do here is update the pg_class row; the new options will be
14989 * propagated into relcaches during post-commit cache inval.
14991 memset(repl_val
, 0, sizeof(repl_val
));
14992 memset(repl_null
, false, sizeof(repl_null
));
14993 memset(repl_repl
, false, sizeof(repl_repl
));
14995 if (newOptions
!= (Datum
) 0)
14996 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
14998 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15000 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15002 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15003 repl_val
, repl_null
, repl_repl
);
15005 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15007 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15009 heap_freetuple(newtuple
);
15011 ReleaseSysCache(tuple
);
15013 /* repeat the whole exercise for the toast table, if there's one */
15014 if (OidIsValid(rel
->rd_rel
->reltoastrelid
))
15017 Oid toastid
= rel
->rd_rel
->reltoastrelid
;
15019 toastrel
= table_open(toastid
, lockmode
);
15021 /* Fetch heap tuple */
15022 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(toastid
));
15023 if (!HeapTupleIsValid(tuple
))
15024 elog(ERROR
, "cache lookup failed for relation %u", toastid
);
15026 if (operation
== AT_ReplaceRelOptions
)
15029 * If we're supposed to replace the reloptions list, we just
15030 * pretend there were none before.
15037 /* Get the old reloptions */
15038 datum
= SysCacheGetAttr(RELOID
, tuple
, Anum_pg_class_reloptions
,
15042 newOptions
= transformRelOptions(isnull
? (Datum
) 0 : datum
,
15043 defList
, "toast", validnsps
, false,
15044 operation
== AT_ResetRelOptions
);
15046 (void) heap_reloptions(RELKIND_TOASTVALUE
, newOptions
, true);
15048 memset(repl_val
, 0, sizeof(repl_val
));
15049 memset(repl_null
, false, sizeof(repl_null
));
15050 memset(repl_repl
, false, sizeof(repl_repl
));
15052 if (newOptions
!= (Datum
) 0)
15053 repl_val
[Anum_pg_class_reloptions
- 1] = newOptions
;
15055 repl_null
[Anum_pg_class_reloptions
- 1] = true;
15057 repl_repl
[Anum_pg_class_reloptions
- 1] = true;
15059 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(pgclass
),
15060 repl_val
, repl_null
, repl_repl
);
15062 CatalogTupleUpdate(pgclass
, &newtuple
->t_self
, newtuple
);
15064 InvokeObjectPostAlterHookArg(RelationRelationId
,
15065 RelationGetRelid(toastrel
), 0,
15068 heap_freetuple(newtuple
);
15070 ReleaseSysCache(tuple
);
15072 table_close(toastrel
, NoLock
);
15075 table_close(pgclass
, RowExclusiveLock
);
15079 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15080 * rewriting to be done, so we just want to copy the data as fast as possible.
15083 ATExecSetTableSpace(Oid tableOid
, Oid newTableSpace
, LOCKMODE lockmode
)
15087 RelFileNumber newrelfilenumber
;
15088 RelFileLocator newrlocator
;
15089 List
*reltoastidxids
= NIL
;
15093 * Need lock here in case we are recursing to toast table or index
15095 rel
= relation_open(tableOid
, lockmode
);
15097 /* Check first if relation can be moved to new tablespace */
15098 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15100 InvokeObjectPostAlterHook(RelationRelationId
,
15101 RelationGetRelid(rel
), 0);
15102 relation_close(rel
, NoLock
);
15106 reltoastrelid
= rel
->rd_rel
->reltoastrelid
;
15107 /* Fetch the list of indexes on toast relation if necessary */
15108 if (OidIsValid(reltoastrelid
))
15110 Relation toastRel
= relation_open(reltoastrelid
, lockmode
);
15112 reltoastidxids
= RelationGetIndexList(toastRel
);
15113 relation_close(toastRel
, lockmode
);
15117 * Relfilenumbers are not unique in databases across tablespaces, so we
15118 * need to allocate a new one in the new tablespace.
15120 newrelfilenumber
= GetNewRelFileNumber(newTableSpace
, NULL
,
15121 rel
->rd_rel
->relpersistence
);
15123 /* Open old and new relation */
15124 newrlocator
= rel
->rd_locator
;
15125 newrlocator
.relNumber
= newrelfilenumber
;
15126 newrlocator
.spcOid
= newTableSpace
;
15128 /* hand off to AM to actually create new rel storage and copy the data */
15129 if (rel
->rd_rel
->relkind
== RELKIND_INDEX
)
15131 index_copy_data(rel
, newrlocator
);
15135 Assert(RELKIND_HAS_TABLE_AM(rel
->rd_rel
->relkind
));
15136 table_relation_copy_data(rel
, &newrlocator
);
15140 * Update the pg_class row.
15142 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15143 * executed on pg_class or its indexes (the above copy wouldn't contain
15144 * the updated pg_class entry), but that's forbidden with
15145 * CheckRelationTableSpaceMove().
15147 SetRelationTableSpace(rel
, newTableSpace
, newrelfilenumber
);
15149 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15151 RelationAssumeNewRelfilelocator(rel
);
15153 relation_close(rel
, NoLock
);
15155 /* Make sure the reltablespace change is visible */
15156 CommandCounterIncrement();
15158 /* Move associated toast relation and/or indexes, too */
15159 if (OidIsValid(reltoastrelid
))
15160 ATExecSetTableSpace(reltoastrelid
, newTableSpace
, lockmode
);
15161 foreach(lc
, reltoastidxids
)
15162 ATExecSetTableSpace(lfirst_oid(lc
), newTableSpace
, lockmode
);
15165 list_free(reltoastidxids
);
15169 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15170 * storage that have an interest in preserving tablespace.
15172 * Since these have no storage the tablespace can be updated with a simple
15173 * metadata only operation to update the tablespace.
15176 ATExecSetTableSpaceNoStorage(Relation rel
, Oid newTableSpace
)
15179 * Shouldn't be called on relations having storage; these are processed in
15182 Assert(!RELKIND_HAS_STORAGE(rel
->rd_rel
->relkind
));
15184 /* check if relation can be moved to its new tablespace */
15185 if (!CheckRelationTableSpaceMove(rel
, newTableSpace
))
15187 InvokeObjectPostAlterHook(RelationRelationId
,
15188 RelationGetRelid(rel
),
15193 /* Update can be done, so change reltablespace */
15194 SetRelationTableSpace(rel
, newTableSpace
, InvalidOid
);
15196 InvokeObjectPostAlterHook(RelationRelationId
, RelationGetRelid(rel
), 0);
15198 /* Make sure the reltablespace change is visible */
15199 CommandCounterIncrement();
15203 * Alter Table ALL ... SET TABLESPACE
15205 * Allows a user to move all objects of some type in a given tablespace in the
15206 * current database to another tablespace. Objects can be chosen based on the
15207 * owner of the object also, to allow users to move only their objects.
15208 * The user must have CREATE rights on the new tablespace, as usual. The main
15209 * permissions handling is done by the lower-level table move function.
15211 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15212 * lock can't be acquired then we ereport(ERROR).
15215 AlterTableMoveAll(AlterTableMoveAllStmt
*stmt
)
15217 List
*relations
= NIL
;
15219 ScanKeyData key
[1];
15221 TableScanDesc scan
;
15223 Oid orig_tablespaceoid
;
15224 Oid new_tablespaceoid
;
15225 List
*role_oids
= roleSpecsToIds(stmt
->roles
);
15227 /* Ensure we were not asked to move something we can't */
15228 if (stmt
->objtype
!= OBJECT_TABLE
&& stmt
->objtype
!= OBJECT_INDEX
&&
15229 stmt
->objtype
!= OBJECT_MATVIEW
)
15231 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15232 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15234 /* Get the orig and new tablespace OIDs */
15235 orig_tablespaceoid
= get_tablespace_oid(stmt
->orig_tablespacename
, false);
15236 new_tablespaceoid
= get_tablespace_oid(stmt
->new_tablespacename
, false);
15238 /* Can't move shared relations in to or out of pg_global */
15239 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15240 if (orig_tablespaceoid
== GLOBALTABLESPACE_OID
||
15241 new_tablespaceoid
== GLOBALTABLESPACE_OID
)
15243 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
15244 errmsg("cannot move relations in to or out of pg_global tablespace")));
15247 * Must have CREATE rights on the new tablespace, unless it is the
15248 * database default tablespace (which all users implicitly have CREATE
15251 if (OidIsValid(new_tablespaceoid
) && new_tablespaceoid
!= MyDatabaseTableSpace
)
15253 AclResult aclresult
;
15255 aclresult
= object_aclcheck(TableSpaceRelationId
, new_tablespaceoid
, GetUserId(),
15257 if (aclresult
!= ACLCHECK_OK
)
15258 aclcheck_error(aclresult
, OBJECT_TABLESPACE
,
15259 get_tablespace_name(new_tablespaceoid
));
15263 * Now that the checks are done, check if we should set either to
15264 * InvalidOid because it is our database's default tablespace.
15266 if (orig_tablespaceoid
== MyDatabaseTableSpace
)
15267 orig_tablespaceoid
= InvalidOid
;
15269 if (new_tablespaceoid
== MyDatabaseTableSpace
)
15270 new_tablespaceoid
= InvalidOid
;
15273 if (orig_tablespaceoid
== new_tablespaceoid
)
15274 return new_tablespaceoid
;
15277 * Walk the list of objects in the tablespace and move them. This will
15278 * only find objects in our database, of course.
15280 ScanKeyInit(&key
[0],
15281 Anum_pg_class_reltablespace
,
15282 BTEqualStrategyNumber
, F_OIDEQ
,
15283 ObjectIdGetDatum(orig_tablespaceoid
));
15285 rel
= table_open(RelationRelationId
, AccessShareLock
);
15286 scan
= table_beginscan_catalog(rel
, 1, key
);
15287 while ((tuple
= heap_getnext(scan
, ForwardScanDirection
)) != NULL
)
15289 Form_pg_class relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
15290 Oid relOid
= relForm
->oid
;
15293 * Do not move objects in pg_catalog as part of this, if an admin
15294 * really wishes to do so, they can issue the individual ALTER
15295 * commands directly.
15297 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15298 * (TOAST will be moved with the main table).
15300 if (IsCatalogNamespace(relForm
->relnamespace
) ||
15301 relForm
->relisshared
||
15302 isAnyTempNamespace(relForm
->relnamespace
) ||
15303 IsToastNamespace(relForm
->relnamespace
))
15306 /* Only move the object type requested */
15307 if ((stmt
->objtype
== OBJECT_TABLE
&&
15308 relForm
->relkind
!= RELKIND_RELATION
&&
15309 relForm
->relkind
!= RELKIND_PARTITIONED_TABLE
) ||
15310 (stmt
->objtype
== OBJECT_INDEX
&&
15311 relForm
->relkind
!= RELKIND_INDEX
&&
15312 relForm
->relkind
!= RELKIND_PARTITIONED_INDEX
) ||
15313 (stmt
->objtype
== OBJECT_MATVIEW
&&
15314 relForm
->relkind
!= RELKIND_MATVIEW
))
15317 /* Check if we are only moving objects owned by certain roles */
15318 if (role_oids
!= NIL
&& !list_member_oid(role_oids
, relForm
->relowner
))
15322 * Handle permissions-checking here since we are locking the tables
15323 * and also to avoid doing a bunch of work only to fail part-way. Note
15324 * that permissions will also be checked by AlterTableInternal().
15326 * Caller must be considered an owner on the table to move it.
15328 if (!object_ownercheck(RelationRelationId
, relOid
, GetUserId()))
15329 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relOid
)),
15330 NameStr(relForm
->relname
));
15332 if (stmt
->nowait
&&
15333 !ConditionalLockRelationOid(relOid
, AccessExclusiveLock
))
15335 (errcode(ERRCODE_OBJECT_IN_USE
),
15336 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15337 get_namespace_name(relForm
->relnamespace
),
15338 NameStr(relForm
->relname
))));
15340 LockRelationOid(relOid
, AccessExclusiveLock
);
15342 /* Add to our list of objects to move */
15343 relations
= lappend_oid(relations
, relOid
);
15346 table_endscan(scan
);
15347 table_close(rel
, AccessShareLock
);
15349 if (relations
== NIL
)
15351 (errcode(ERRCODE_NO_DATA_FOUND
),
15352 errmsg("no matching relations in tablespace \"%s\" found",
15353 orig_tablespaceoid
== InvalidOid
? "(database default)" :
15354 get_tablespace_name(orig_tablespaceoid
))));
15356 /* Everything is locked, loop through and move all of the relations. */
15357 foreach(l
, relations
)
15360 AlterTableCmd
*cmd
= makeNode(AlterTableCmd
);
15362 cmd
->subtype
= AT_SetTableSpace
;
15363 cmd
->name
= stmt
->new_tablespacename
;
15365 cmds
= lappend(cmds
, cmd
);
15367 EventTriggerAlterTableStart((Node
*) stmt
);
15368 /* OID is set by AlterTableInternal */
15369 AlterTableInternal(lfirst_oid(l
), cmds
, false);
15370 EventTriggerAlterTableEnd();
15373 return new_tablespaceoid
;
15377 index_copy_data(Relation rel
, RelFileLocator newrlocator
)
15379 SMgrRelation dstrel
;
15382 * Since we copy the file directly without looking at the shared buffers,
15383 * we'd better first flush out any pages of the source relation that are
15384 * in shared buffers. We assume no new changes will be made while we are
15385 * holding exclusive lock on the rel.
15387 FlushRelationBuffers(rel
);
15390 * Create and copy all forks of the relation, and schedule unlinking of
15391 * old physical files.
15393 * NOTE: any conflict in relfilenumber value will be caught in
15394 * RelationCreateStorage().
15396 dstrel
= RelationCreateStorage(newrlocator
, rel
->rd_rel
->relpersistence
, true);
15398 /* copy main fork */
15399 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, MAIN_FORKNUM
,
15400 rel
->rd_rel
->relpersistence
);
15402 /* copy those extra forks that exist */
15403 for (ForkNumber forkNum
= MAIN_FORKNUM
+ 1;
15404 forkNum
<= MAX_FORKNUM
; forkNum
++)
15406 if (smgrexists(RelationGetSmgr(rel
), forkNum
))
15408 smgrcreate(dstrel
, forkNum
, false);
15411 * WAL log creation if the relation is persistent, or this is the
15412 * init fork of an unlogged relation.
15414 if (RelationIsPermanent(rel
) ||
15415 (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_UNLOGGED
&&
15416 forkNum
== INIT_FORKNUM
))
15417 log_smgrcreate(&newrlocator
, forkNum
);
15418 RelationCopyStorage(RelationGetSmgr(rel
), dstrel
, forkNum
,
15419 rel
->rd_rel
->relpersistence
);
15423 /* drop old relation, and close new one */
15424 RelationDropStorage(rel
);
15429 * ALTER TABLE ENABLE/DISABLE TRIGGER
15431 * We just pass this off to trigger.c.
15434 ATExecEnableDisableTrigger(Relation rel
, const char *trigname
,
15435 char fires_when
, bool skip_system
, bool recurse
,
15438 EnableDisableTrigger(rel
, trigname
, InvalidOid
,
15439 fires_when
, skip_system
, recurse
,
15442 InvokeObjectPostAlterHook(RelationRelationId
,
15443 RelationGetRelid(rel
), 0);
15447 * ALTER TABLE ENABLE/DISABLE RULE
15449 * We just pass this off to rewriteDefine.c.
15452 ATExecEnableDisableRule(Relation rel
, const char *rulename
,
15453 char fires_when
, LOCKMODE lockmode
)
15455 EnableDisableRule(rel
, rulename
, fires_when
);
15457 InvokeObjectPostAlterHook(RelationRelationId
,
15458 RelationGetRelid(rel
), 0);
15462 * ALTER TABLE INHERIT
15464 * Add a parent to the child's parents. This verifies that all the columns and
15465 * check constraints of the parent appear in the child and that they have the
15466 * same data types and expressions.
15469 ATPrepAddInherit(Relation child_rel
)
15471 if (child_rel
->rd_rel
->reloftype
)
15473 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15474 errmsg("cannot change inheritance of typed table")));
15476 if (child_rel
->rd_rel
->relispartition
)
15478 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15479 errmsg("cannot change inheritance of a partition")));
15481 if (child_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15483 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15484 errmsg("cannot change inheritance of partitioned table")));
15488 * Return the address of the new parent relation.
15490 static ObjectAddress
15491 ATExecAddInherit(Relation child_rel
, RangeVar
*parent
, LOCKMODE lockmode
)
15493 Relation parent_rel
;
15495 ObjectAddress address
;
15496 const char *trigger_name
;
15499 * A self-exclusive lock is needed here. See the similar case in
15500 * MergeAttributes() for a full explanation.
15502 parent_rel
= table_openrv(parent
, ShareUpdateExclusiveLock
);
15505 * Must be owner of both parent and child -- child was checked by
15506 * ATSimplePermissions call in ATPrepCmd
15508 ATSimplePermissions(AT_AddInherit
, parent_rel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
15510 /* Permanent rels cannot inherit from temporary ones */
15511 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15512 child_rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
15514 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15515 errmsg("cannot inherit from temporary relation \"%s\"",
15516 RelationGetRelationName(parent_rel
))));
15518 /* If parent rel is temp, it must belong to this session */
15519 if (parent_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15520 !parent_rel
->rd_islocaltemp
)
15522 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15523 errmsg("cannot inherit from temporary relation of another session")));
15525 /* Ditto for the child */
15526 if (child_rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
15527 !child_rel
->rd_islocaltemp
)
15529 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15530 errmsg("cannot inherit to temporary relation of another session")));
15532 /* Prevent partitioned tables from becoming inheritance parents */
15533 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15535 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15536 errmsg("cannot inherit from partitioned table \"%s\"",
15537 parent
->relname
)));
15539 /* Likewise for partitions */
15540 if (parent_rel
->rd_rel
->relispartition
)
15542 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15543 errmsg("cannot inherit from a partition")));
15546 * Prevent circularity by seeing if proposed parent inherits from child.
15547 * (In particular, this disallows making a rel inherit from itself.)
15549 * This is not completely bulletproof because of race conditions: in
15550 * multi-level inheritance trees, someone else could concurrently be
15551 * making another inheritance link that closes the loop but does not join
15552 * either of the rels we have locked. Preventing that seems to require
15553 * exclusive locks on the entire inheritance tree, which is a cure worse
15554 * than the disease. find_all_inheritors() will cope with circularity
15555 * anyway, so don't sweat it too much.
15557 * We use weakest lock we can on child's children, namely AccessShareLock.
15559 children
= find_all_inheritors(RelationGetRelid(child_rel
),
15560 AccessShareLock
, NULL
);
15562 if (list_member_oid(children
, RelationGetRelid(parent_rel
)))
15564 (errcode(ERRCODE_DUPLICATE_TABLE
),
15565 errmsg("circular inheritance not allowed"),
15566 errdetail("\"%s\" is already a child of \"%s\".",
15568 RelationGetRelationName(child_rel
))));
15571 * If child_rel has row-level triggers with transition tables, we
15572 * currently don't allow it to become an inheritance child. See also
15573 * prohibitions in ATExecAttachPartition() and CreateTrigger().
15575 trigger_name
= FindTriggerIncompatibleWithInheritance(child_rel
->trigdesc
);
15576 if (trigger_name
!= NULL
)
15578 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
15579 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15580 trigger_name
, RelationGetRelationName(child_rel
)),
15581 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15583 /* OK to create inheritance */
15584 CreateInheritance(child_rel
, parent_rel
, false);
15586 ObjectAddressSet(address
, RelationRelationId
,
15587 RelationGetRelid(parent_rel
));
15589 /* keep our lock on the parent relation until commit */
15590 table_close(parent_rel
, NoLock
);
15596 * CreateInheritance
15597 * Catalog manipulation portion of creating inheritance between a child
15598 * table and a parent table.
15600 * Common to ATExecAddInherit() and ATExecAttachPartition().
15603 CreateInheritance(Relation child_rel
, Relation parent_rel
, bool ispartition
)
15605 Relation catalogRelation
;
15608 HeapTuple inheritsTuple
;
15611 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15612 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
15615 * Check for duplicates in the list of parents, and determine the highest
15616 * inhseqno already present; we'll use the next one for the new parent.
15617 * Also, if proposed child is a partition, it cannot already be
15620 * Note: we do not reject the case where the child already inherits from
15621 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15624 Anum_pg_inherits_inhrelid
,
15625 BTEqualStrategyNumber
, F_OIDEQ
,
15626 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15627 scan
= systable_beginscan(catalogRelation
, InheritsRelidSeqnoIndexId
,
15628 true, NULL
, 1, &key
);
15630 /* inhseqno sequences start at 1 */
15632 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
15634 Form_pg_inherits inh
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
15636 if (inh
->inhparent
== RelationGetRelid(parent_rel
))
15638 (errcode(ERRCODE_DUPLICATE_TABLE
),
15639 errmsg("relation \"%s\" would be inherited from more than once",
15640 RelationGetRelationName(parent_rel
))));
15642 if (inh
->inhseqno
> inhseqno
)
15643 inhseqno
= inh
->inhseqno
;
15645 systable_endscan(scan
);
15647 /* Match up the columns and bump attinhcount as needed */
15648 MergeAttributesIntoExisting(child_rel
, parent_rel
, ispartition
);
15650 /* Match up the constraints and bump coninhcount as needed */
15651 MergeConstraintsIntoExisting(child_rel
, parent_rel
);
15654 * OK, it looks valid. Make the catalog entries that show inheritance.
15656 StoreCatalogInheritance1(RelationGetRelid(child_rel
),
15657 RelationGetRelid(parent_rel
),
15660 parent_rel
->rd_rel
->relkind
==
15661 RELKIND_PARTITIONED_TABLE
);
15663 /* Now we're done with pg_inherits */
15664 table_close(catalogRelation
, RowExclusiveLock
);
15668 * Obtain the source-text form of the constraint expression for a check
15669 * constraint, given its pg_constraint tuple
15672 decompile_conbin(HeapTuple contup
, TupleDesc tupdesc
)
15674 Form_pg_constraint con
;
15679 con
= (Form_pg_constraint
) GETSTRUCT(contup
);
15680 attr
= heap_getattr(contup
, Anum_pg_constraint_conbin
, tupdesc
, &isnull
);
15682 elog(ERROR
, "null conbin for constraint %u", con
->oid
);
15684 expr
= DirectFunctionCall2(pg_get_expr
, attr
,
15685 ObjectIdGetDatum(con
->conrelid
));
15686 return TextDatumGetCString(expr
);
15690 * Determine whether two check constraints are functionally equivalent
15692 * The test we apply is to see whether they reverse-compile to the same
15693 * source string. This insulates us from issues like whether attributes
15694 * have the same physical column numbers in parent and child relations.
15697 constraints_equivalent(HeapTuple a
, HeapTuple b
, TupleDesc tupleDesc
)
15699 Form_pg_constraint acon
= (Form_pg_constraint
) GETSTRUCT(a
);
15700 Form_pg_constraint bcon
= (Form_pg_constraint
) GETSTRUCT(b
);
15702 if (acon
->condeferrable
!= bcon
->condeferrable
||
15703 acon
->condeferred
!= bcon
->condeferred
||
15704 strcmp(decompile_conbin(a
, tupleDesc
),
15705 decompile_conbin(b
, tupleDesc
)) != 0)
15712 * Check columns in child table match up with columns in parent, and increment
15713 * their attinhcount.
15715 * Called by CreateInheritance
15717 * Currently all parent columns must be found in child. Missing columns are an
15718 * error. One day we might consider creating new columns like CREATE TABLE
15719 * does. However, that is widely unpopular --- in the common use case of
15720 * partitioned tables it's a foot-gun.
15722 * The data type must match exactly. If the parent column is NOT NULL then
15723 * the child must be as well. Defaults are not compared, however.
15726 MergeAttributesIntoExisting(Relation child_rel
, Relation parent_rel
, bool ispartition
)
15729 TupleDesc parent_desc
;
15731 attrrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
15732 parent_desc
= RelationGetDescr(parent_rel
);
15734 for (AttrNumber parent_attno
= 1; parent_attno
<= parent_desc
->natts
; parent_attno
++)
15736 Form_pg_attribute parent_att
= TupleDescAttr(parent_desc
, parent_attno
- 1);
15737 char *parent_attname
= NameStr(parent_att
->attname
);
15740 /* Ignore dropped columns in the parent. */
15741 if (parent_att
->attisdropped
)
15744 /* Find same column in child (matching on column name). */
15745 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(child_rel
), parent_attname
);
15746 if (HeapTupleIsValid(tuple
))
15748 Form_pg_attribute child_att
= (Form_pg_attribute
) GETSTRUCT(tuple
);
15750 if (parent_att
->atttypid
!= child_att
->atttypid
||
15751 parent_att
->atttypmod
!= child_att
->atttypmod
)
15753 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15754 errmsg("child table \"%s\" has different type for column \"%s\"",
15755 RelationGetRelationName(child_rel
), parent_attname
)));
15757 if (parent_att
->attcollation
!= child_att
->attcollation
)
15759 (errcode(ERRCODE_COLLATION_MISMATCH
),
15760 errmsg("child table \"%s\" has different collation for column \"%s\"",
15761 RelationGetRelationName(child_rel
), parent_attname
)));
15764 * Check child doesn't discard NOT NULL property. (Other
15765 * constraints are checked elsewhere.)
15767 if (parent_att
->attnotnull
&& !child_att
->attnotnull
)
15769 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15770 errmsg("column \"%s\" in child table must be marked NOT NULL",
15774 * Child column must be generated if and only if parent column is.
15776 if (parent_att
->attgenerated
&& !child_att
->attgenerated
)
15778 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15779 errmsg("column \"%s\" in child table must be a generated column", parent_attname
)));
15780 if (child_att
->attgenerated
&& !parent_att
->attgenerated
)
15782 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15783 errmsg("column \"%s\" in child table must not be a generated column", parent_attname
)));
15786 * Regular inheritance children are independent enough not to
15787 * inherit identity columns. But partitions are integral part of
15788 * a partitioned table and inherit identity column.
15791 child_att
->attidentity
= parent_att
->attidentity
;
15794 * OK, bump the child column's inheritance count. (If we fail
15795 * later on, this change will just roll back.)
15797 child_att
->attinhcount
++;
15798 if (child_att
->attinhcount
< 0)
15800 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
15801 errmsg("too many inheritance parents"));
15804 * In case of partitions, we must enforce that value of attislocal
15805 * is same in all partitions. (Note: there are only inherited
15806 * attributes in partitions)
15808 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15810 Assert(child_att
->attinhcount
== 1);
15811 child_att
->attislocal
= false;
15814 CatalogTupleUpdate(attrrel
, &tuple
->t_self
, tuple
);
15815 heap_freetuple(tuple
);
15820 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15821 errmsg("child table is missing column \"%s\"", parent_attname
)));
15825 table_close(attrrel
, RowExclusiveLock
);
15829 * Check constraints in child table match up with constraints in parent,
15830 * and increment their coninhcount.
15832 * Constraints that are marked ONLY in the parent are ignored.
15834 * Called by CreateInheritance
15836 * Currently all constraints in parent must be present in the child. One day we
15837 * may consider adding new constraints like CREATE TABLE does.
15839 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15840 * constraints. As long as tables have more like 10 constraints it shouldn't be
15841 * a problem though. Even 100 constraints ought not be the end of the world.
15843 * XXX See MergeWithExistingConstraint too if you change this code.
15846 MergeConstraintsIntoExisting(Relation child_rel
, Relation parent_rel
)
15848 Relation constraintrel
;
15849 SysScanDesc parent_scan
;
15850 ScanKeyData parent_key
;
15851 HeapTuple parent_tuple
;
15852 Oid parent_relid
= RelationGetRelid(parent_rel
);
15854 constraintrel
= table_open(ConstraintRelationId
, RowExclusiveLock
);
15856 /* Outer loop scans through the parent's constraint definitions */
15857 ScanKeyInit(&parent_key
,
15858 Anum_pg_constraint_conrelid
,
15859 BTEqualStrategyNumber
, F_OIDEQ
,
15860 ObjectIdGetDatum(parent_relid
));
15861 parent_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
15862 true, NULL
, 1, &parent_key
);
15864 while (HeapTupleIsValid(parent_tuple
= systable_getnext(parent_scan
)))
15866 Form_pg_constraint parent_con
= (Form_pg_constraint
) GETSTRUCT(parent_tuple
);
15867 SysScanDesc child_scan
;
15868 ScanKeyData child_key
;
15869 HeapTuple child_tuple
;
15870 bool found
= false;
15872 if (parent_con
->contype
!= CONSTRAINT_CHECK
)
15875 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15876 if (parent_con
->connoinherit
)
15879 /* Search for a child constraint matching this one */
15880 ScanKeyInit(&child_key
,
15881 Anum_pg_constraint_conrelid
,
15882 BTEqualStrategyNumber
, F_OIDEQ
,
15883 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
15884 child_scan
= systable_beginscan(constraintrel
, ConstraintRelidTypidNameIndexId
,
15885 true, NULL
, 1, &child_key
);
15887 while (HeapTupleIsValid(child_tuple
= systable_getnext(child_scan
)))
15889 Form_pg_constraint child_con
= (Form_pg_constraint
) GETSTRUCT(child_tuple
);
15890 HeapTuple child_copy
;
15892 if (child_con
->contype
!= CONSTRAINT_CHECK
)
15895 if (strcmp(NameStr(parent_con
->conname
),
15896 NameStr(child_con
->conname
)) != 0)
15899 if (!constraints_equivalent(parent_tuple
, child_tuple
, RelationGetDescr(constraintrel
)))
15901 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15902 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15903 RelationGetRelationName(child_rel
), NameStr(parent_con
->conname
))));
15905 /* If the child constraint is "no inherit" then cannot merge */
15906 if (child_con
->connoinherit
)
15908 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
15909 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15910 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
15913 * If the child constraint is "not valid" then cannot merge with a
15914 * valid parent constraint
15916 if (parent_con
->convalidated
&& !child_con
->convalidated
)
15918 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
15919 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15920 NameStr(child_con
->conname
), RelationGetRelationName(child_rel
))));
15923 * OK, bump the child constraint's inheritance count. (If we fail
15924 * later on, this change will just roll back.)
15926 child_copy
= heap_copytuple(child_tuple
);
15927 child_con
= (Form_pg_constraint
) GETSTRUCT(child_copy
);
15928 child_con
->coninhcount
++;
15929 if (child_con
->coninhcount
< 0)
15931 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
15932 errmsg("too many inheritance parents"));
15935 * In case of partitions, an inherited constraint must be
15936 * inherited only once since it cannot have multiple parents and
15937 * it is never considered local.
15939 if (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
15941 Assert(child_con
->coninhcount
== 1);
15942 child_con
->conislocal
= false;
15945 CatalogTupleUpdate(constraintrel
, &child_copy
->t_self
, child_copy
);
15946 heap_freetuple(child_copy
);
15952 systable_endscan(child_scan
);
15956 (errcode(ERRCODE_DATATYPE_MISMATCH
),
15957 errmsg("child table is missing constraint \"%s\"",
15958 NameStr(parent_con
->conname
))));
15961 systable_endscan(parent_scan
);
15962 table_close(constraintrel
, RowExclusiveLock
);
15966 * ALTER TABLE NO INHERIT
15968 * Return value is the address of the relation that is no longer parent.
15970 static ObjectAddress
15971 ATExecDropInherit(Relation rel
, RangeVar
*parent
, LOCKMODE lockmode
)
15973 ObjectAddress address
;
15974 Relation parent_rel
;
15976 if (rel
->rd_rel
->relispartition
)
15978 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
15979 errmsg("cannot change inheritance of a partition")));
15982 * AccessShareLock on the parent is probably enough, seeing that DROP
15983 * TABLE doesn't lock parent tables at all. We need some lock since we'll
15984 * be inspecting the parent's schema.
15986 parent_rel
= table_openrv(parent
, AccessShareLock
);
15989 * We don't bother to check ownership of the parent table --- ownership of
15990 * the child is presumed enough rights.
15993 /* Off to RemoveInheritance() where most of the work happens */
15994 RemoveInheritance(rel
, parent_rel
, false);
15996 ObjectAddressSet(address
, RelationRelationId
,
15997 RelationGetRelid(parent_rel
));
15999 /* keep our lock on the parent relation until commit */
16000 table_close(parent_rel
, NoLock
);
16006 * MarkInheritDetached
16008 * Set inhdetachpending for a partition, for ATExecDetachPartition
16009 * in concurrent mode. While at it, verify that no other partition is
16010 * already pending detach.
16013 MarkInheritDetached(Relation child_rel
, Relation parent_rel
)
16015 Relation catalogRelation
;
16018 HeapTuple inheritsTuple
;
16019 bool found
= false;
16021 Assert(parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16024 * Find pg_inherits entries by inhparent. (We need to scan them all in
16025 * order to verify that no other partition is pending detach.)
16027 catalogRelation
= table_open(InheritsRelationId
, RowExclusiveLock
);
16029 Anum_pg_inherits_inhparent
,
16030 BTEqualStrategyNumber
, F_OIDEQ
,
16031 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16032 scan
= systable_beginscan(catalogRelation
, InheritsParentIndexId
,
16033 true, NULL
, 1, &key
);
16035 while (HeapTupleIsValid(inheritsTuple
= systable_getnext(scan
)))
16037 Form_pg_inherits inhForm
;
16039 inhForm
= (Form_pg_inherits
) GETSTRUCT(inheritsTuple
);
16040 if (inhForm
->inhdetachpending
)
16042 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16043 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16044 get_rel_name(inhForm
->inhrelid
),
16045 get_namespace_name(parent_rel
->rd_rel
->relnamespace
),
16046 RelationGetRelationName(parent_rel
)),
16047 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16049 if (inhForm
->inhrelid
== RelationGetRelid(child_rel
))
16053 newtup
= heap_copytuple(inheritsTuple
);
16054 ((Form_pg_inherits
) GETSTRUCT(newtup
))->inhdetachpending
= true;
16056 CatalogTupleUpdate(catalogRelation
,
16057 &inheritsTuple
->t_self
,
16060 heap_freetuple(newtup
);
16061 /* keep looking, to ensure we catch others pending detach */
16066 systable_endscan(scan
);
16067 table_close(catalogRelation
, RowExclusiveLock
);
16071 (errcode(ERRCODE_UNDEFINED_TABLE
),
16072 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16073 RelationGetRelationName(child_rel
),
16074 RelationGetRelationName(parent_rel
))));
16078 * RemoveInheritance
16080 * Drop a parent from the child's parents. This just adjusts the attinhcount
16081 * and attislocal of the columns and removes the pg_inherit and pg_depend
16082 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16084 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16085 * up attislocal stays true, which means if a child is ever removed from a
16086 * parent then its columns will never be automatically dropped which may
16087 * surprise. But at least we'll never surprise by dropping columns someone
16088 * isn't expecting to be dropped which would actually mean data loss.
16090 * coninhcount and conislocal for inherited constraints are adjusted in
16091 * exactly the same way.
16093 * Common to ATExecDropInherit() and ATExecDetachPartition().
16096 RemoveInheritance(Relation child_rel
, Relation parent_rel
, bool expect_detached
)
16098 Relation catalogRelation
;
16100 ScanKeyData key
[3];
16101 HeapTuple attributeTuple
,
16105 bool is_partitioning
;
16107 is_partitioning
= (parent_rel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
);
16109 found
= DeleteInheritsTuple(RelationGetRelid(child_rel
),
16110 RelationGetRelid(parent_rel
),
16112 RelationGetRelationName(child_rel
));
16115 if (is_partitioning
)
16117 (errcode(ERRCODE_UNDEFINED_TABLE
),
16118 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16119 RelationGetRelationName(child_rel
),
16120 RelationGetRelationName(parent_rel
))));
16123 (errcode(ERRCODE_UNDEFINED_TABLE
),
16124 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16125 RelationGetRelationName(parent_rel
),
16126 RelationGetRelationName(child_rel
))));
16130 * Search through child columns looking for ones matching parent rel
16132 catalogRelation
= table_open(AttributeRelationId
, RowExclusiveLock
);
16133 ScanKeyInit(&key
[0],
16134 Anum_pg_attribute_attrelid
,
16135 BTEqualStrategyNumber
, F_OIDEQ
,
16136 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16137 scan
= systable_beginscan(catalogRelation
, AttributeRelidNumIndexId
,
16138 true, NULL
, 1, key
);
16139 while (HeapTupleIsValid(attributeTuple
= systable_getnext(scan
)))
16141 Form_pg_attribute att
= (Form_pg_attribute
) GETSTRUCT(attributeTuple
);
16143 /* Ignore if dropped or not inherited */
16144 if (att
->attisdropped
)
16146 if (att
->attinhcount
<= 0)
16149 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel
),
16150 NameStr(att
->attname
)))
16152 /* Decrement inhcount and possibly set islocal to true */
16153 HeapTuple copyTuple
= heap_copytuple(attributeTuple
);
16154 Form_pg_attribute copy_att
= (Form_pg_attribute
) GETSTRUCT(copyTuple
);
16156 copy_att
->attinhcount
--;
16157 if (copy_att
->attinhcount
== 0)
16158 copy_att
->attislocal
= true;
16160 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16161 heap_freetuple(copyTuple
);
16164 systable_endscan(scan
);
16165 table_close(catalogRelation
, RowExclusiveLock
);
16168 * Likewise, find inherited check constraints and disinherit them. To do
16169 * this, we first need a list of the names of the parent's check
16170 * constraints. (We cheat a bit by only checking for name matches,
16171 * assuming that the expressions will match.)
16173 catalogRelation
= table_open(ConstraintRelationId
, RowExclusiveLock
);
16174 ScanKeyInit(&key
[0],
16175 Anum_pg_constraint_conrelid
,
16176 BTEqualStrategyNumber
, F_OIDEQ
,
16177 ObjectIdGetDatum(RelationGetRelid(parent_rel
)));
16178 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16179 true, NULL
, 1, key
);
16183 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16185 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16187 if (con
->contype
== CONSTRAINT_CHECK
)
16188 connames
= lappend(connames
, pstrdup(NameStr(con
->conname
)));
16191 systable_endscan(scan
);
16193 /* Now scan the child's constraints */
16194 ScanKeyInit(&key
[0],
16195 Anum_pg_constraint_conrelid
,
16196 BTEqualStrategyNumber
, F_OIDEQ
,
16197 ObjectIdGetDatum(RelationGetRelid(child_rel
)));
16198 scan
= systable_beginscan(catalogRelation
, ConstraintRelidTypidNameIndexId
,
16199 true, NULL
, 1, key
);
16201 while (HeapTupleIsValid(constraintTuple
= systable_getnext(scan
)))
16203 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(constraintTuple
);
16206 if (con
->contype
!= CONSTRAINT_CHECK
)
16210 foreach_ptr(char, chkname
, connames
)
16212 if (strcmp(NameStr(con
->conname
), chkname
) == 0)
16221 /* Decrement inhcount and possibly set islocal to true */
16222 HeapTuple copyTuple
= heap_copytuple(constraintTuple
);
16223 Form_pg_constraint copy_con
= (Form_pg_constraint
) GETSTRUCT(copyTuple
);
16225 if (copy_con
->coninhcount
<= 0) /* shouldn't happen */
16226 elog(ERROR
, "relation %u has non-inherited constraint \"%s\"",
16227 RelationGetRelid(child_rel
), NameStr(copy_con
->conname
));
16229 copy_con
->coninhcount
--;
16230 if (copy_con
->coninhcount
== 0)
16231 copy_con
->conislocal
= true;
16233 CatalogTupleUpdate(catalogRelation
, ©Tuple
->t_self
, copyTuple
);
16234 heap_freetuple(copyTuple
);
16238 systable_endscan(scan
);
16239 table_close(catalogRelation
, RowExclusiveLock
);
16241 drop_parent_dependency(RelationGetRelid(child_rel
),
16242 RelationRelationId
,
16243 RelationGetRelid(parent_rel
),
16244 child_dependency_type(is_partitioning
));
16247 * Post alter hook of this inherits. Since object_access_hook doesn't take
16248 * multiple object identifiers, we relay oid of parent relation using
16249 * auxiliary_id argument.
16251 InvokeObjectPostAlterHookArg(InheritsRelationId
,
16252 RelationGetRelid(child_rel
), 0,
16253 RelationGetRelid(parent_rel
), false);
16257 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16258 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16259 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16260 * be TypeRelationId). There's no convenient way to do this, so go trawling
16261 * through pg_depend.
16264 drop_parent_dependency(Oid relid
, Oid refclassid
, Oid refobjid
,
16265 DependencyType deptype
)
16267 Relation catalogRelation
;
16269 ScanKeyData key
[3];
16270 HeapTuple depTuple
;
16272 catalogRelation
= table_open(DependRelationId
, RowExclusiveLock
);
16274 ScanKeyInit(&key
[0],
16275 Anum_pg_depend_classid
,
16276 BTEqualStrategyNumber
, F_OIDEQ
,
16277 ObjectIdGetDatum(RelationRelationId
));
16278 ScanKeyInit(&key
[1],
16279 Anum_pg_depend_objid
,
16280 BTEqualStrategyNumber
, F_OIDEQ
,
16281 ObjectIdGetDatum(relid
));
16282 ScanKeyInit(&key
[2],
16283 Anum_pg_depend_objsubid
,
16284 BTEqualStrategyNumber
, F_INT4EQ
,
16287 scan
= systable_beginscan(catalogRelation
, DependDependerIndexId
, true,
16290 while (HeapTupleIsValid(depTuple
= systable_getnext(scan
)))
16292 Form_pg_depend dep
= (Form_pg_depend
) GETSTRUCT(depTuple
);
16294 if (dep
->refclassid
== refclassid
&&
16295 dep
->refobjid
== refobjid
&&
16296 dep
->refobjsubid
== 0 &&
16297 dep
->deptype
== deptype
)
16298 CatalogTupleDelete(catalogRelation
, &depTuple
->t_self
);
16301 systable_endscan(scan
);
16302 table_close(catalogRelation
, RowExclusiveLock
);
16308 * Attach a table to a composite type, as though it had been created with CREATE
16309 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16310 * subject table must not have inheritance parents. These restrictions ensure
16311 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16313 * The address of the type is returned.
16315 static ObjectAddress
16316 ATExecAddOf(Relation rel
, const TypeName
*ofTypename
, LOCKMODE lockmode
)
16318 Oid relid
= RelationGetRelid(rel
);
16320 Form_pg_type typeform
;
16322 Relation inheritsRelation
,
16326 AttrNumber table_attno
,
16328 TupleDesc typeTupleDesc
,
16330 ObjectAddress tableobj
,
16332 HeapTuple classtuple
;
16334 /* Validate the type. */
16335 typetuple
= typenameType(NULL
, ofTypename
, NULL
);
16336 check_of_type(typetuple
);
16337 typeform
= (Form_pg_type
) GETSTRUCT(typetuple
);
16338 typeid = typeform
->oid
;
16340 /* Fail if the table has any inheritance parents. */
16341 inheritsRelation
= table_open(InheritsRelationId
, AccessShareLock
);
16343 Anum_pg_inherits_inhrelid
,
16344 BTEqualStrategyNumber
, F_OIDEQ
,
16345 ObjectIdGetDatum(relid
));
16346 scan
= systable_beginscan(inheritsRelation
, InheritsRelidSeqnoIndexId
,
16347 true, NULL
, 1, &key
);
16348 if (HeapTupleIsValid(systable_getnext(scan
)))
16350 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16351 errmsg("typed tables cannot inherit")));
16352 systable_endscan(scan
);
16353 table_close(inheritsRelation
, AccessShareLock
);
16356 * Check the tuple descriptors for compatibility. Unlike inheritance, we
16357 * require that the order also match. However, attnotnull need not match.
16359 typeTupleDesc
= lookup_rowtype_tupdesc(typeid, -1);
16360 tableTupleDesc
= RelationGetDescr(rel
);
16362 for (type_attno
= 1; type_attno
<= typeTupleDesc
->natts
; type_attno
++)
16364 Form_pg_attribute type_attr
,
16366 const char *type_attname
,
16369 /* Get the next non-dropped type attribute. */
16370 type_attr
= TupleDescAttr(typeTupleDesc
, type_attno
- 1);
16371 if (type_attr
->attisdropped
)
16373 type_attname
= NameStr(type_attr
->attname
);
16375 /* Get the next non-dropped table attribute. */
16378 if (table_attno
> tableTupleDesc
->natts
)
16380 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16381 errmsg("table is missing column \"%s\"",
16383 table_attr
= TupleDescAttr(tableTupleDesc
, table_attno
- 1);
16385 } while (table_attr
->attisdropped
);
16386 table_attname
= NameStr(table_attr
->attname
);
16388 /* Compare name. */
16389 if (strncmp(table_attname
, type_attname
, NAMEDATALEN
) != 0)
16391 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16392 errmsg("table has column \"%s\" where type requires \"%s\"",
16393 table_attname
, type_attname
)));
16395 /* Compare type. */
16396 if (table_attr
->atttypid
!= type_attr
->atttypid
||
16397 table_attr
->atttypmod
!= type_attr
->atttypmod
||
16398 table_attr
->attcollation
!= type_attr
->attcollation
)
16400 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16401 errmsg("table \"%s\" has different type for column \"%s\"",
16402 RelationGetRelationName(rel
), type_attname
)));
16404 ReleaseTupleDesc(typeTupleDesc
);
16406 /* Any remaining columns at the end of the table had better be dropped. */
16407 for (; table_attno
<= tableTupleDesc
->natts
; table_attno
++)
16409 Form_pg_attribute table_attr
= TupleDescAttr(tableTupleDesc
,
16412 if (!table_attr
->attisdropped
)
16414 (errcode(ERRCODE_DATATYPE_MISMATCH
),
16415 errmsg("table has extra column \"%s\"",
16416 NameStr(table_attr
->attname
))));
16419 /* If the table was already typed, drop the existing dependency. */
16420 if (rel
->rd_rel
->reloftype
)
16421 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
16422 DEPENDENCY_NORMAL
);
16424 /* Record a dependency on the new type. */
16425 tableobj
.classId
= RelationRelationId
;
16426 tableobj
.objectId
= relid
;
16427 tableobj
.objectSubId
= 0;
16428 typeobj
.classId
= TypeRelationId
;
16429 typeobj
.objectId
= typeid;
16430 typeobj
.objectSubId
= 0;
16431 recordDependencyOn(&tableobj
, &typeobj
, DEPENDENCY_NORMAL
);
16433 /* Update pg_class.reloftype */
16434 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
16435 classtuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16436 if (!HeapTupleIsValid(classtuple
))
16437 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16438 ((Form_pg_class
) GETSTRUCT(classtuple
))->reloftype
= typeid;
16439 CatalogTupleUpdate(relationRelation
, &classtuple
->t_self
, classtuple
);
16441 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
16443 heap_freetuple(classtuple
);
16444 table_close(relationRelation
, RowExclusiveLock
);
16446 ReleaseSysCache(typetuple
);
16452 * ALTER TABLE NOT OF
16454 * Detach a typed table from its originating type. Just clear reloftype and
16455 * remove the dependency.
16458 ATExecDropOf(Relation rel
, LOCKMODE lockmode
)
16460 Oid relid
= RelationGetRelid(rel
);
16461 Relation relationRelation
;
16464 if (!OidIsValid(rel
->rd_rel
->reloftype
))
16466 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16467 errmsg("\"%s\" is not a typed table",
16468 RelationGetRelationName(rel
))));
16471 * We don't bother to check ownership of the type --- ownership of the
16472 * table is presumed enough rights. No lock required on the type, either.
16475 drop_parent_dependency(relid
, TypeRelationId
, rel
->rd_rel
->reloftype
,
16476 DEPENDENCY_NORMAL
);
16478 /* Clear pg_class.reloftype */
16479 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
16480 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16481 if (!HeapTupleIsValid(tuple
))
16482 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16483 ((Form_pg_class
) GETSTRUCT(tuple
))->reloftype
= InvalidOid
;
16484 CatalogTupleUpdate(relationRelation
, &tuple
->t_self
, tuple
);
16486 InvokeObjectPostAlterHook(RelationRelationId
, relid
, 0);
16488 heap_freetuple(tuple
);
16489 table_close(relationRelation
, RowExclusiveLock
);
16493 * relation_mark_replica_identity: Update a table's replica identity
16495 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16496 * index. Otherwise, it must be InvalidOid.
16498 * Caller had better hold an exclusive lock on the relation, as the results
16499 * of running two of these concurrently wouldn't be pretty.
16502 relation_mark_replica_identity(Relation rel
, char ri_type
, Oid indexOid
,
16507 HeapTuple pg_class_tuple
;
16508 HeapTuple pg_index_tuple
;
16509 Form_pg_class pg_class_form
;
16510 Form_pg_index pg_index_form
;
16514 * Check whether relreplident has changed, and update it if so.
16516 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16517 pg_class_tuple
= SearchSysCacheCopy1(RELOID
,
16518 ObjectIdGetDatum(RelationGetRelid(rel
)));
16519 if (!HeapTupleIsValid(pg_class_tuple
))
16520 elog(ERROR
, "cache lookup failed for relation \"%s\"",
16521 RelationGetRelationName(rel
));
16522 pg_class_form
= (Form_pg_class
) GETSTRUCT(pg_class_tuple
);
16523 if (pg_class_form
->relreplident
!= ri_type
)
16525 pg_class_form
->relreplident
= ri_type
;
16526 CatalogTupleUpdate(pg_class
, &pg_class_tuple
->t_self
, pg_class_tuple
);
16528 table_close(pg_class
, RowExclusiveLock
);
16529 heap_freetuple(pg_class_tuple
);
16532 * Update the per-index indisreplident flags correctly.
16534 pg_index
= table_open(IndexRelationId
, RowExclusiveLock
);
16535 foreach(index
, RelationGetIndexList(rel
))
16537 Oid thisIndexOid
= lfirst_oid(index
);
16538 bool dirty
= false;
16540 pg_index_tuple
= SearchSysCacheCopy1(INDEXRELID
,
16541 ObjectIdGetDatum(thisIndexOid
));
16542 if (!HeapTupleIsValid(pg_index_tuple
))
16543 elog(ERROR
, "cache lookup failed for index %u", thisIndexOid
);
16544 pg_index_form
= (Form_pg_index
) GETSTRUCT(pg_index_tuple
);
16546 if (thisIndexOid
== indexOid
)
16548 /* Set the bit if not already set. */
16549 if (!pg_index_form
->indisreplident
)
16552 pg_index_form
->indisreplident
= true;
16557 /* Unset the bit if set. */
16558 if (pg_index_form
->indisreplident
)
16561 pg_index_form
->indisreplident
= false;
16567 CatalogTupleUpdate(pg_index
, &pg_index_tuple
->t_self
, pg_index_tuple
);
16568 InvokeObjectPostAlterHookArg(IndexRelationId
, thisIndexOid
, 0,
16569 InvalidOid
, is_internal
);
16572 * Invalidate the relcache for the table, so that after we commit
16573 * all sessions will refresh the table's replica identity index
16574 * before attempting any UPDATE or DELETE on the table. (If we
16575 * changed the table's pg_class row above, then a relcache inval
16576 * is already queued due to that; but we might not have.)
16578 CacheInvalidateRelcache(rel
);
16580 heap_freetuple(pg_index_tuple
);
16583 table_close(pg_index
, RowExclusiveLock
);
16587 * ALTER TABLE <name> REPLICA IDENTITY ...
16590 ATExecReplicaIdentity(Relation rel
, ReplicaIdentityStmt
*stmt
, LOCKMODE lockmode
)
16596 if (stmt
->identity_type
== REPLICA_IDENTITY_DEFAULT
)
16598 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16601 else if (stmt
->identity_type
== REPLICA_IDENTITY_FULL
)
16603 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16606 else if (stmt
->identity_type
== REPLICA_IDENTITY_NOTHING
)
16608 relation_mark_replica_identity(rel
, stmt
->identity_type
, InvalidOid
, true);
16611 else if (stmt
->identity_type
== REPLICA_IDENTITY_INDEX
)
16613 /* fallthrough */ ;
16616 elog(ERROR
, "unexpected identity type %u", stmt
->identity_type
);
16618 /* Check that the index exists */
16619 indexOid
= get_relname_relid(stmt
->name
, rel
->rd_rel
->relnamespace
);
16620 if (!OidIsValid(indexOid
))
16622 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16623 errmsg("index \"%s\" for table \"%s\" does not exist",
16624 stmt
->name
, RelationGetRelationName(rel
))));
16626 indexRel
= index_open(indexOid
, ShareLock
);
16628 /* Check that the index is on the relation we're altering. */
16629 if (indexRel
->rd_index
== NULL
||
16630 indexRel
->rd_index
->indrelid
!= RelationGetRelid(rel
))
16632 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16633 errmsg("\"%s\" is not an index for table \"%s\"",
16634 RelationGetRelationName(indexRel
),
16635 RelationGetRelationName(rel
))));
16636 /* The AM must support uniqueness, and the index must in fact be unique. */
16637 if (!indexRel
->rd_indam
->amcanunique
||
16638 !indexRel
->rd_index
->indisunique
)
16640 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16641 errmsg("cannot use non-unique index \"%s\" as replica identity",
16642 RelationGetRelationName(indexRel
))));
16643 /* Deferred indexes are not guaranteed to be always unique. */
16644 if (!indexRel
->rd_index
->indimmediate
)
16646 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16647 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16648 RelationGetRelationName(indexRel
))));
16649 /* Expression indexes aren't supported. */
16650 if (RelationGetIndexExpressions(indexRel
) != NIL
)
16652 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16653 errmsg("cannot use expression index \"%s\" as replica identity",
16654 RelationGetRelationName(indexRel
))));
16655 /* Predicate indexes aren't supported. */
16656 if (RelationGetIndexPredicate(indexRel
) != NIL
)
16658 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16659 errmsg("cannot use partial index \"%s\" as replica identity",
16660 RelationGetRelationName(indexRel
))));
16662 /* Check index for nullable columns. */
16663 for (key
= 0; key
< IndexRelationGetNumberOfKeyAttributes(indexRel
); key
++)
16665 int16 attno
= indexRel
->rd_index
->indkey
.values
[key
];
16666 Form_pg_attribute attr
;
16669 * Reject any other system columns. (Going forward, we'll disallow
16670 * indexes containing such columns in the first place, but they might
16671 * exist in older branches.)
16675 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE
),
16676 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16677 RelationGetRelationName(indexRel
), attno
)));
16679 attr
= TupleDescAttr(rel
->rd_att
, attno
- 1);
16680 if (!attr
->attnotnull
)
16682 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
16683 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16684 RelationGetRelationName(indexRel
),
16685 NameStr(attr
->attname
))));
16688 /* This index is suitable for use as a replica identity. Mark it. */
16689 relation_mark_replica_identity(rel
, stmt
->identity_type
, indexOid
, true);
16691 index_close(indexRel
, NoLock
);
16695 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16698 ATExecSetRowSecurity(Relation rel
, bool rls
)
16704 relid
= RelationGetRelid(rel
);
16706 /* Pull the record for this relation and update it */
16707 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16709 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16711 if (!HeapTupleIsValid(tuple
))
16712 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16714 ((Form_pg_class
) GETSTRUCT(tuple
))->relrowsecurity
= rls
;
16715 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16717 InvokeObjectPostAlterHook(RelationRelationId
,
16718 RelationGetRelid(rel
), 0);
16720 table_close(pg_class
, RowExclusiveLock
);
16721 heap_freetuple(tuple
);
16725 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16728 ATExecForceNoForceRowSecurity(Relation rel
, bool force_rls
)
16734 relid
= RelationGetRelid(rel
);
16736 pg_class
= table_open(RelationRelationId
, RowExclusiveLock
);
16738 tuple
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relid
));
16740 if (!HeapTupleIsValid(tuple
))
16741 elog(ERROR
, "cache lookup failed for relation %u", relid
);
16743 ((Form_pg_class
) GETSTRUCT(tuple
))->relforcerowsecurity
= force_rls
;
16744 CatalogTupleUpdate(pg_class
, &tuple
->t_self
, tuple
);
16746 InvokeObjectPostAlterHook(RelationRelationId
,
16747 RelationGetRelid(rel
), 0);
16749 table_close(pg_class
, RowExclusiveLock
);
16750 heap_freetuple(tuple
);
16754 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16757 ATExecGenericOptions(Relation rel
, List
*options
)
16760 ForeignServer
*server
;
16761 ForeignDataWrapper
*fdw
;
16764 Datum repl_val
[Natts_pg_foreign_table
];
16765 bool repl_null
[Natts_pg_foreign_table
];
16766 bool repl_repl
[Natts_pg_foreign_table
];
16768 Form_pg_foreign_table tableform
;
16770 if (options
== NIL
)
16773 ftrel
= table_open(ForeignTableRelationId
, RowExclusiveLock
);
16775 tuple
= SearchSysCacheCopy1(FOREIGNTABLEREL
,
16776 ObjectIdGetDatum(rel
->rd_id
));
16777 if (!HeapTupleIsValid(tuple
))
16779 (errcode(ERRCODE_UNDEFINED_OBJECT
),
16780 errmsg("foreign table \"%s\" does not exist",
16781 RelationGetRelationName(rel
))));
16782 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tuple
);
16783 server
= GetForeignServer(tableform
->ftserver
);
16784 fdw
= GetForeignDataWrapper(server
->fdwid
);
16786 memset(repl_val
, 0, sizeof(repl_val
));
16787 memset(repl_null
, false, sizeof(repl_null
));
16788 memset(repl_repl
, false, sizeof(repl_repl
));
16790 /* Extract the current options */
16791 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
16793 Anum_pg_foreign_table_ftoptions
,
16796 datum
= PointerGetDatum(NULL
);
16798 /* Transform the options */
16799 datum
= transformGenericOptions(ForeignTableRelationId
,
16802 fdw
->fdwvalidator
);
16804 if (PointerIsValid(DatumGetPointer(datum
)))
16805 repl_val
[Anum_pg_foreign_table_ftoptions
- 1] = datum
;
16807 repl_null
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16809 repl_repl
[Anum_pg_foreign_table_ftoptions
- 1] = true;
16811 /* Everything looks good - update the tuple */
16813 tuple
= heap_modify_tuple(tuple
, RelationGetDescr(ftrel
),
16814 repl_val
, repl_null
, repl_repl
);
16816 CatalogTupleUpdate(ftrel
, &tuple
->t_self
, tuple
);
16819 * Invalidate relcache so that all sessions will refresh any cached plans
16820 * that might depend on the old options.
16822 CacheInvalidateRelcache(rel
);
16824 InvokeObjectPostAlterHook(ForeignTableRelationId
,
16825 RelationGetRelid(rel
), 0);
16827 table_close(ftrel
, RowExclusiveLock
);
16829 heap_freetuple(tuple
);
16833 * ALTER TABLE ALTER COLUMN SET COMPRESSION
16835 * Return value is the address of the modified column
16837 static ObjectAddress
16838 ATExecSetCompression(Relation rel
,
16839 const char *column
,
16845 Form_pg_attribute atttableform
;
16849 ObjectAddress address
;
16851 compression
= strVal(newValue
);
16853 attrel
= table_open(AttributeRelationId
, RowExclusiveLock
);
16855 /* copy the cache entry so we can scribble on it below */
16856 tuple
= SearchSysCacheCopyAttName(RelationGetRelid(rel
), column
);
16857 if (!HeapTupleIsValid(tuple
))
16859 (errcode(ERRCODE_UNDEFINED_COLUMN
),
16860 errmsg("column \"%s\" of relation \"%s\" does not exist",
16861 column
, RelationGetRelationName(rel
))));
16863 /* prevent them from altering a system attribute */
16864 atttableform
= (Form_pg_attribute
) GETSTRUCT(tuple
);
16865 attnum
= atttableform
->attnum
;
16868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
16869 errmsg("cannot alter system column \"%s\"", column
)));
16872 * Check that column type is compressible, then get the attribute
16873 * compression method code
16875 cmethod
= GetAttributeCompression(atttableform
->atttypid
, compression
);
16877 /* update pg_attribute entry */
16878 atttableform
->attcompression
= cmethod
;
16879 CatalogTupleUpdate(attrel
, &tuple
->t_self
, tuple
);
16881 InvokeObjectPostAlterHook(RelationRelationId
,
16882 RelationGetRelid(rel
),
16886 * Apply the change to indexes as well (only for simple index columns,
16887 * matching behavior of index.c ConstructTupleDescriptor()).
16889 SetIndexStorageProperties(rel
, attrel
, attnum
,
16894 heap_freetuple(tuple
);
16896 table_close(attrel
, RowExclusiveLock
);
16898 /* make changes visible */
16899 CommandCounterIncrement();
16901 ObjectAddressSubSet(address
, RelationRelationId
,
16902 RelationGetRelid(rel
), attnum
);
16908 * Preparation phase for SET LOGGED/UNLOGGED
16910 * This verifies that we're not trying to change a temp table. Also,
16911 * existing foreign key constraints are checked to avoid ending up with
16912 * permanent tables referencing unlogged tables.
16914 * Return value is false if the operation is a no-op (in which case the
16915 * checks are skipped), otherwise true.
16918 ATPrepChangePersistence(Relation rel
, bool toLogged
)
16920 Relation pg_constraint
;
16923 ScanKeyData skey
[1];
16926 * Disallow changing status for a temp table. Also verify whether we can
16927 * get away with doing nothing; in such cases we don't need to run the
16928 * checks below, either.
16930 switch (rel
->rd_rel
->relpersistence
)
16932 case RELPERSISTENCE_TEMP
:
16934 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
16935 errmsg("cannot change logged status of table \"%s\" because it is temporary",
16936 RelationGetRelationName(rel
)),
16939 case RELPERSISTENCE_PERMANENT
:
16941 /* nothing to do */
16944 case RELPERSISTENCE_UNLOGGED
:
16946 /* nothing to do */
16952 * Check that the table is not part of any publication when changing to
16953 * UNLOGGED, as UNLOGGED tables can't be published.
16956 GetRelationPublications(RelationGetRelid(rel
)) != NIL
)
16958 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
16959 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16960 RelationGetRelationName(rel
)),
16961 errdetail("Unlogged relations cannot be replicated.")));
16964 * Check existing foreign key constraints to preserve the invariant that
16965 * permanent tables cannot reference unlogged ones. Self-referencing
16966 * foreign keys can safely be ignored.
16968 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
16971 * Scan conrelid if changing to permanent, else confrelid. This also
16972 * determines whether a useful index exists.
16974 ScanKeyInit(&skey
[0],
16975 toLogged
? Anum_pg_constraint_conrelid
:
16976 Anum_pg_constraint_confrelid
,
16977 BTEqualStrategyNumber
, F_OIDEQ
,
16978 ObjectIdGetDatum(RelationGetRelid(rel
)));
16979 scan
= systable_beginscan(pg_constraint
,
16980 toLogged
? ConstraintRelidTypidNameIndexId
: InvalidOid
,
16981 true, NULL
, 1, skey
);
16983 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
16985 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
16987 if (con
->contype
== CONSTRAINT_FOREIGN
)
16990 Relation foreignrel
;
16992 /* the opposite end of what we used as scankey */
16993 foreignrelid
= toLogged
? con
->confrelid
: con
->conrelid
;
16995 /* ignore if self-referencing */
16996 if (RelationGetRelid(rel
) == foreignrelid
)
16999 foreignrel
= relation_open(foreignrelid
, AccessShareLock
);
17003 if (!RelationIsPermanent(foreignrel
))
17005 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17006 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17007 RelationGetRelationName(rel
),
17008 RelationGetRelationName(foreignrel
)),
17009 errtableconstraint(rel
, NameStr(con
->conname
))));
17013 if (RelationIsPermanent(foreignrel
))
17015 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
17016 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17017 RelationGetRelationName(rel
),
17018 RelationGetRelationName(foreignrel
)),
17019 errtableconstraint(rel
, NameStr(con
->conname
))));
17022 relation_close(foreignrel
, AccessShareLock
);
17026 systable_endscan(scan
);
17028 table_close(pg_constraint
, AccessShareLock
);
17034 * Execute ALTER TABLE SET SCHEMA
17037 AlterTableNamespace(AlterObjectSchemaStmt
*stmt
, Oid
*oldschema
)
17044 ObjectAddresses
*objsMoved
;
17045 ObjectAddress myself
;
17047 relid
= RangeVarGetRelidExtended(stmt
->relation
, AccessExclusiveLock
,
17048 stmt
->missing_ok
? RVR_MISSING_OK
: 0,
17049 RangeVarCallbackForAlterRelation
,
17052 if (!OidIsValid(relid
))
17055 (errmsg("relation \"%s\" does not exist, skipping",
17056 stmt
->relation
->relname
)));
17057 return InvalidObjectAddress
;
17060 rel
= relation_open(relid
, NoLock
);
17062 oldNspOid
= RelationGetNamespace(rel
);
17064 /* If it's an owned sequence, disallow moving it by itself. */
17065 if (rel
->rd_rel
->relkind
== RELKIND_SEQUENCE
)
17070 if (sequenceIsOwned(relid
, DEPENDENCY_AUTO
, &tableId
, &colId
) ||
17071 sequenceIsOwned(relid
, DEPENDENCY_INTERNAL
, &tableId
, &colId
))
17073 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
17074 errmsg("cannot move an owned sequence into another schema"),
17075 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17076 RelationGetRelationName(rel
),
17077 get_rel_name(tableId
))));
17080 /* Get and lock schema OID and check its permissions. */
17081 newrv
= makeRangeVar(stmt
->newschema
, RelationGetRelationName(rel
), -1);
17082 nspOid
= RangeVarGetAndCheckCreationNamespace(newrv
, NoLock
, NULL
);
17084 /* common checks on switching namespaces */
17085 CheckSetNamespace(oldNspOid
, nspOid
);
17087 objsMoved
= new_object_addresses();
17088 AlterTableNamespaceInternal(rel
, oldNspOid
, nspOid
, objsMoved
);
17089 free_object_addresses(objsMoved
);
17091 ObjectAddressSet(myself
, RelationRelationId
, relid
);
17094 *oldschema
= oldNspOid
;
17096 /* close rel, but keep lock until commit */
17097 relation_close(rel
, NoLock
);
17103 * The guts of relocating a table or materialized view to another namespace:
17104 * besides moving the relation itself, its dependent objects are relocated to
17108 AlterTableNamespaceInternal(Relation rel
, Oid oldNspOid
, Oid nspOid
,
17109 ObjectAddresses
*objsMoved
)
17113 Assert(objsMoved
!= NULL
);
17115 /* OK, modify the pg_class row and pg_depend entry */
17116 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
17118 AlterRelationNamespaceInternal(classRel
, RelationGetRelid(rel
), oldNspOid
,
17119 nspOid
, true, objsMoved
);
17121 /* Fix the table's row type too, if it has one */
17122 if (OidIsValid(rel
->rd_rel
->reltype
))
17123 AlterTypeNamespaceInternal(rel
->rd_rel
->reltype
, nspOid
,
17124 false, /* isImplicitArray */
17125 false, /* ignoreDependent */
17126 false, /* errorOnTableType */
17129 /* Fix other dependent stuff */
17130 AlterIndexNamespaces(classRel
, rel
, oldNspOid
, nspOid
, objsMoved
);
17131 AlterSeqNamespaces(classRel
, rel
, oldNspOid
, nspOid
,
17132 objsMoved
, AccessExclusiveLock
);
17133 AlterConstraintNamespaces(RelationGetRelid(rel
), oldNspOid
, nspOid
,
17136 table_close(classRel
, RowExclusiveLock
);
17140 * The guts of relocating a relation to another namespace: fix the pg_class
17141 * entry, and the pg_depend entry if any. Caller must already have
17142 * opened and write-locked pg_class.
17145 AlterRelationNamespaceInternal(Relation classRel
, Oid relOid
,
17146 Oid oldNspOid
, Oid newNspOid
,
17147 bool hasDependEntry
,
17148 ObjectAddresses
*objsMoved
)
17150 HeapTuple classTup
;
17151 Form_pg_class classForm
;
17152 ObjectAddress thisobj
;
17153 bool already_done
= false;
17155 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(relOid
));
17156 if (!HeapTupleIsValid(classTup
))
17157 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
17158 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
17160 Assert(classForm
->relnamespace
== oldNspOid
);
17162 thisobj
.classId
= RelationRelationId
;
17163 thisobj
.objectId
= relOid
;
17164 thisobj
.objectSubId
= 0;
17167 * If the object has already been moved, don't move it again. If it's
17168 * already in the right place, don't move it, but still fire the object
17171 already_done
= object_address_present(&thisobj
, objsMoved
);
17172 if (!already_done
&& oldNspOid
!= newNspOid
)
17174 /* check for duplicate name (more friendly than unique-index failure) */
17175 if (get_relname_relid(NameStr(classForm
->relname
),
17176 newNspOid
) != InvalidOid
)
17178 (errcode(ERRCODE_DUPLICATE_TABLE
),
17179 errmsg("relation \"%s\" already exists in schema \"%s\"",
17180 NameStr(classForm
->relname
),
17181 get_namespace_name(newNspOid
))));
17183 /* classTup is a copy, so OK to scribble on */
17184 classForm
->relnamespace
= newNspOid
;
17186 CatalogTupleUpdate(classRel
, &classTup
->t_self
, classTup
);
17188 /* Update dependency on schema if caller said so */
17189 if (hasDependEntry
&&
17190 changeDependencyFor(RelationRelationId
,
17192 NamespaceRelationId
,
17195 elog(ERROR
, "could not change schema dependency for relation \"%s\"",
17196 NameStr(classForm
->relname
));
17200 add_exact_object_address(&thisobj
, objsMoved
);
17202 InvokeObjectPostAlterHook(RelationRelationId
, relOid
, 0);
17205 heap_freetuple(classTup
);
17209 * Move all indexes for the specified relation to another namespace.
17211 * Note: we assume adequate permission checking was done by the caller,
17212 * and that the caller has a suitable lock on the owning relation.
17215 AlterIndexNamespaces(Relation classRel
, Relation rel
,
17216 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
)
17221 indexList
= RelationGetIndexList(rel
);
17223 foreach(l
, indexList
)
17225 Oid indexOid
= lfirst_oid(l
);
17226 ObjectAddress thisobj
;
17228 thisobj
.classId
= RelationRelationId
;
17229 thisobj
.objectId
= indexOid
;
17230 thisobj
.objectSubId
= 0;
17233 * Note: currently, the index will not have its own dependency on the
17234 * namespace, so we don't need to do changeDependencyFor(). There's no
17235 * row type in pg_type, either.
17237 * XXX this objsMoved test may be pointless -- surely we have a single
17238 * dependency link from a relation to each index?
17240 if (!object_address_present(&thisobj
, objsMoved
))
17242 AlterRelationNamespaceInternal(classRel
, indexOid
,
17243 oldNspOid
, newNspOid
,
17245 add_exact_object_address(&thisobj
, objsMoved
);
17249 list_free(indexList
);
17253 * Move all identity and SERIAL-column sequences of the specified relation to another
17256 * Note: we assume adequate permission checking was done by the caller,
17257 * and that the caller has a suitable lock on the owning relation.
17260 AlterSeqNamespaces(Relation classRel
, Relation rel
,
17261 Oid oldNspOid
, Oid newNspOid
, ObjectAddresses
*objsMoved
,
17266 ScanKeyData key
[2];
17270 * SERIAL sequences are those having an auto dependency on one of the
17271 * table's columns (we don't care *which* column, exactly).
17273 depRel
= table_open(DependRelationId
, AccessShareLock
);
17275 ScanKeyInit(&key
[0],
17276 Anum_pg_depend_refclassid
,
17277 BTEqualStrategyNumber
, F_OIDEQ
,
17278 ObjectIdGetDatum(RelationRelationId
));
17279 ScanKeyInit(&key
[1],
17280 Anum_pg_depend_refobjid
,
17281 BTEqualStrategyNumber
, F_OIDEQ
,
17282 ObjectIdGetDatum(RelationGetRelid(rel
)));
17283 /* we leave refobjsubid unspecified */
17285 scan
= systable_beginscan(depRel
, DependReferenceIndexId
, true,
17288 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
17290 Form_pg_depend depForm
= (Form_pg_depend
) GETSTRUCT(tup
);
17293 /* skip dependencies other than auto dependencies on columns */
17294 if (depForm
->refobjsubid
== 0 ||
17295 depForm
->classid
!= RelationRelationId
||
17296 depForm
->objsubid
!= 0 ||
17297 !(depForm
->deptype
== DEPENDENCY_AUTO
|| depForm
->deptype
== DEPENDENCY_INTERNAL
))
17300 /* Use relation_open just in case it's an index */
17301 seqRel
= relation_open(depForm
->objid
, lockmode
);
17303 /* skip non-sequence relations */
17304 if (RelationGetForm(seqRel
)->relkind
!= RELKIND_SEQUENCE
)
17306 /* No need to keep the lock */
17307 relation_close(seqRel
, lockmode
);
17311 /* Fix the pg_class and pg_depend entries */
17312 AlterRelationNamespaceInternal(classRel
, depForm
->objid
,
17313 oldNspOid
, newNspOid
,
17317 * Sequences used to have entries in pg_type, but no longer do. If we
17318 * ever re-instate that, we'll need to move the pg_type entry to the
17319 * new namespace, too (using AlterTypeNamespaceInternal).
17321 Assert(RelationGetForm(seqRel
)->reltype
== InvalidOid
);
17323 /* Now we can close it. Keep the lock till end of transaction. */
17324 relation_close(seqRel
, NoLock
);
17327 systable_endscan(scan
);
17329 relation_close(depRel
, AccessShareLock
);
17334 * This code supports
17335 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17337 * Because we only support this for TEMP tables, it's sufficient to remember
17338 * the state in a backend-local data structure.
17342 * Register a newly-created relation's ON COMMIT action.
17345 register_on_commit_action(Oid relid
, OnCommitAction action
)
17348 MemoryContext oldcxt
;
17351 * We needn't bother registering the relation unless there is an ON COMMIT
17352 * action we need to take.
17354 if (action
== ONCOMMIT_NOOP
|| action
== ONCOMMIT_PRESERVE_ROWS
)
17357 oldcxt
= MemoryContextSwitchTo(CacheMemoryContext
);
17359 oc
= (OnCommitItem
*) palloc(sizeof(OnCommitItem
));
17361 oc
->oncommit
= action
;
17362 oc
->creating_subid
= GetCurrentSubTransactionId();
17363 oc
->deleting_subid
= InvalidSubTransactionId
;
17366 * We use lcons() here so that ON COMMIT actions are processed in reverse
17367 * order of registration. That might not be essential but it seems
17370 on_commits
= lcons(oc
, on_commits
);
17372 MemoryContextSwitchTo(oldcxt
);
17376 * Unregister any ON COMMIT action when a relation is deleted.
17378 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
17381 remove_on_commit_action(Oid relid
)
17385 foreach(l
, on_commits
)
17387 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17389 if (oc
->relid
== relid
)
17391 oc
->deleting_subid
= GetCurrentSubTransactionId();
17398 * Perform ON COMMIT actions.
17400 * This is invoked just before actually committing, since it's possible
17401 * to encounter errors.
17404 PreCommit_on_commit_actions(void)
17407 List
*oids_to_truncate
= NIL
;
17408 List
*oids_to_drop
= NIL
;
17410 foreach(l
, on_commits
)
17412 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17414 /* Ignore entry if already dropped in this xact */
17415 if (oc
->deleting_subid
!= InvalidSubTransactionId
)
17418 switch (oc
->oncommit
)
17420 case ONCOMMIT_NOOP
:
17421 case ONCOMMIT_PRESERVE_ROWS
:
17422 /* Do nothing (there shouldn't be such entries, actually) */
17424 case ONCOMMIT_DELETE_ROWS
:
17427 * If this transaction hasn't accessed any temporary
17428 * relations, we can skip truncating ON COMMIT DELETE ROWS
17429 * tables, as they must still be empty.
17431 if ((MyXactFlags
& XACT_FLAGS_ACCESSEDTEMPNAMESPACE
))
17432 oids_to_truncate
= lappend_oid(oids_to_truncate
, oc
->relid
);
17434 case ONCOMMIT_DROP
:
17435 oids_to_drop
= lappend_oid(oids_to_drop
, oc
->relid
);
17441 * Truncate relations before dropping so that all dependencies between
17442 * relations are removed after they are worked on. Doing it like this
17443 * might be a waste as it is possible that a relation being truncated will
17444 * be dropped anyway due to its parent being dropped, but this makes the
17445 * code more robust because of not having to re-check that the relation
17446 * exists at truncation time.
17448 if (oids_to_truncate
!= NIL
)
17449 heap_truncate(oids_to_truncate
);
17451 if (oids_to_drop
!= NIL
)
17453 ObjectAddresses
*targetObjects
= new_object_addresses();
17455 foreach(l
, oids_to_drop
)
17457 ObjectAddress object
;
17459 object
.classId
= RelationRelationId
;
17460 object
.objectId
= lfirst_oid(l
);
17461 object
.objectSubId
= 0;
17463 Assert(!object_address_present(&object
, targetObjects
));
17465 add_exact_object_address(&object
, targetObjects
);
17469 * Object deletion might involve toast table access (to clean up
17470 * toasted catalog entries), so ensure we have a valid snapshot.
17472 PushActiveSnapshot(GetTransactionSnapshot());
17475 * Since this is an automatic drop, rather than one directly initiated
17476 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17478 performMultipleDeletions(targetObjects
, DROP_CASCADE
,
17479 PERFORM_DELETION_INTERNAL
| PERFORM_DELETION_QUIETLY
);
17481 PopActiveSnapshot();
17483 #ifdef USE_ASSERT_CHECKING
17486 * Note that table deletion will call remove_on_commit_action, so the
17487 * entry should get marked as deleted.
17489 foreach(l
, on_commits
)
17491 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(l
);
17493 if (oc
->oncommit
!= ONCOMMIT_DROP
)
17496 Assert(oc
->deleting_subid
!= InvalidSubTransactionId
);
17503 * Post-commit or post-abort cleanup for ON COMMIT management.
17505 * All we do here is remove no-longer-needed OnCommitItem entries.
17507 * During commit, remove entries that were deleted during this transaction;
17508 * during abort, remove those created during this transaction.
17511 AtEOXact_on_commit_actions(bool isCommit
)
17513 ListCell
*cur_item
;
17515 foreach(cur_item
, on_commits
)
17517 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
17519 if (isCommit
? oc
->deleting_subid
!= InvalidSubTransactionId
:
17520 oc
->creating_subid
!= InvalidSubTransactionId
)
17522 /* cur_item must be removed */
17523 on_commits
= foreach_delete_current(on_commits
, cur_item
);
17528 /* cur_item must be preserved */
17529 oc
->creating_subid
= InvalidSubTransactionId
;
17530 oc
->deleting_subid
= InvalidSubTransactionId
;
17536 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17538 * During subabort, we can immediately remove entries created during this
17539 * subtransaction. During subcommit, just relabel entries marked during
17540 * this subtransaction as being the parent's responsibility.
17543 AtEOSubXact_on_commit_actions(bool isCommit
, SubTransactionId mySubid
,
17544 SubTransactionId parentSubid
)
17546 ListCell
*cur_item
;
17548 foreach(cur_item
, on_commits
)
17550 OnCommitItem
*oc
= (OnCommitItem
*) lfirst(cur_item
);
17552 if (!isCommit
&& oc
->creating_subid
== mySubid
)
17554 /* cur_item must be removed */
17555 on_commits
= foreach_delete_current(on_commits
, cur_item
);
17560 /* cur_item must be preserved */
17561 if (oc
->creating_subid
== mySubid
)
17562 oc
->creating_subid
= parentSubid
;
17563 if (oc
->deleting_subid
== mySubid
)
17564 oc
->deleting_subid
= isCommit
? parentSubid
: InvalidSubTransactionId
;
17570 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17571 * the relation to be locked only if (1) it's a plain or partitioned table,
17572 * materialized view, or TOAST table and (2) the current user is the owner (or
17573 * the superuser) or has been granted MAINTAIN. This meets the
17574 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
17575 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
17578 RangeVarCallbackMaintainsTable(const RangeVar
*relation
,
17579 Oid relId
, Oid oldRelId
, void *arg
)
17582 AclResult aclresult
;
17584 /* Nothing to do if the relation was not found. */
17585 if (!OidIsValid(relId
))
17589 * If the relation does exist, check whether it's an index. But note that
17590 * the relation might have been dropped between the time we did the name
17591 * lookup and now. In that case, there's nothing to do.
17593 relkind
= get_rel_relkind(relId
);
17596 if (relkind
!= RELKIND_RELATION
&& relkind
!= RELKIND_TOASTVALUE
&&
17597 relkind
!= RELKIND_MATVIEW
&& relkind
!= RELKIND_PARTITIONED_TABLE
)
17599 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17600 errmsg("\"%s\" is not a table or materialized view", relation
->relname
)));
17602 /* Check permissions */
17603 aclresult
= pg_class_aclcheck(relId
, GetUserId(), ACL_MAINTAIN
);
17604 if (aclresult
!= ACLCHECK_OK
)
17605 aclcheck_error(aclresult
,
17606 get_relkind_objtype(get_rel_relkind(relId
)),
17607 relation
->relname
);
17611 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17614 RangeVarCallbackForTruncate(const RangeVar
*relation
,
17615 Oid relId
, Oid oldRelId
, void *arg
)
17619 /* Nothing to do if the relation was not found. */
17620 if (!OidIsValid(relId
))
17623 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17624 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17625 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17627 truncate_check_rel(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17628 truncate_check_perms(relId
, (Form_pg_class
) GETSTRUCT(tuple
));
17630 ReleaseSysCache(tuple
);
17634 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17635 * the owner of the relation, or superuser.
17638 RangeVarCallbackOwnsRelation(const RangeVar
*relation
,
17639 Oid relId
, Oid oldRelId
, void *arg
)
17643 /* Nothing to do if the relation was not found. */
17644 if (!OidIsValid(relId
))
17647 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relId
));
17648 if (!HeapTupleIsValid(tuple
)) /* should not happen */
17649 elog(ERROR
, "cache lookup failed for relation %u", relId
);
17651 if (!object_ownercheck(RelationRelationId
, relId
, GetUserId()))
17652 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relId
)),
17653 relation
->relname
);
17655 if (!allowSystemTableMods
&&
17656 IsSystemClass(relId
, (Form_pg_class
) GETSTRUCT(tuple
)))
17658 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17659 errmsg("permission denied: \"%s\" is a system catalog",
17660 relation
->relname
)));
17662 ReleaseSysCache(tuple
);
17666 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17670 RangeVarCallbackForAlterRelation(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
17673 Node
*stmt
= (Node
*) arg
;
17674 ObjectType reltype
;
17676 Form_pg_class classform
;
17677 AclResult aclresult
;
17680 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
17681 if (!HeapTupleIsValid(tuple
))
17682 return; /* concurrently dropped */
17683 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
17684 relkind
= classform
->relkind
;
17686 /* Must own relation. */
17687 if (!object_ownercheck(RelationRelationId
, relid
, GetUserId()))
17688 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
17690 /* No system table modifications unless explicitly allowed. */
17691 if (!allowSystemTableMods
&& IsSystemClass(relid
, classform
))
17693 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
17694 errmsg("permission denied: \"%s\" is a system catalog",
17698 * Extract the specified relation type from the statement parse tree.
17700 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17701 * have CREATE rights on the containing namespace.
17703 if (IsA(stmt
, RenameStmt
))
17705 aclresult
= object_aclcheck(NamespaceRelationId
, classform
->relnamespace
,
17706 GetUserId(), ACL_CREATE
);
17707 if (aclresult
!= ACLCHECK_OK
)
17708 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
17709 get_namespace_name(classform
->relnamespace
));
17710 reltype
= ((RenameStmt
*) stmt
)->renameType
;
17712 else if (IsA(stmt
, AlterObjectSchemaStmt
))
17713 reltype
= ((AlterObjectSchemaStmt
*) stmt
)->objectType
;
17715 else if (IsA(stmt
, AlterTableStmt
))
17716 reltype
= ((AlterTableStmt
*) stmt
)->objtype
;
17719 elog(ERROR
, "unrecognized node type: %d", (int) nodeTag(stmt
));
17720 reltype
= OBJECT_TABLE
; /* placate compiler */
17724 * For compatibility with prior releases, we allow ALTER TABLE to be used
17725 * with most other types of relations (but not composite types). We allow
17726 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17727 * otherwise. Otherwise, the user must select the correct form of the
17728 * command for the relation at issue.
17730 if (reltype
== OBJECT_SEQUENCE
&& relkind
!= RELKIND_SEQUENCE
)
17732 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17733 errmsg("\"%s\" is not a sequence", rv
->relname
)));
17735 if (reltype
== OBJECT_VIEW
&& relkind
!= RELKIND_VIEW
)
17737 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17738 errmsg("\"%s\" is not a view", rv
->relname
)));
17740 if (reltype
== OBJECT_MATVIEW
&& relkind
!= RELKIND_MATVIEW
)
17742 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17743 errmsg("\"%s\" is not a materialized view", rv
->relname
)));
17745 if (reltype
== OBJECT_FOREIGN_TABLE
&& relkind
!= RELKIND_FOREIGN_TABLE
)
17747 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17748 errmsg("\"%s\" is not a foreign table", rv
->relname
)));
17750 if (reltype
== OBJECT_TYPE
&& relkind
!= RELKIND_COMPOSITE_TYPE
)
17752 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17753 errmsg("\"%s\" is not a composite type", rv
->relname
)));
17755 if (reltype
== OBJECT_INDEX
&& relkind
!= RELKIND_INDEX
&&
17756 relkind
!= RELKIND_PARTITIONED_INDEX
17757 && !IsA(stmt
, RenameStmt
))
17759 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17760 errmsg("\"%s\" is not an index", rv
->relname
)));
17763 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17766 if (reltype
!= OBJECT_TYPE
&& relkind
== RELKIND_COMPOSITE_TYPE
)
17768 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17769 errmsg("\"%s\" is a composite type", rv
->relname
),
17770 /* translator: %s is an SQL ALTER command */
17771 errhint("Use %s instead.",
17775 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17776 * to a different schema, such as indexes and TOAST tables.
17778 if (IsA(stmt
, AlterObjectSchemaStmt
))
17780 if (relkind
== RELKIND_INDEX
|| relkind
== RELKIND_PARTITIONED_INDEX
)
17782 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17783 errmsg("cannot change schema of index \"%s\"",
17785 errhint("Change the schema of the table instead.")));
17786 else if (relkind
== RELKIND_COMPOSITE_TYPE
)
17788 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17789 errmsg("cannot change schema of composite type \"%s\"",
17791 /* translator: %s is an SQL ALTER command */
17792 errhint("Use %s instead.",
17794 else if (relkind
== RELKIND_TOASTVALUE
)
17796 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
17797 errmsg("cannot change schema of TOAST table \"%s\"",
17799 errhint("Change the schema of the table instead.")));
17802 ReleaseSysCache(tuple
);
17806 * Transform any expressions present in the partition key
17808 * Returns a transformed PartitionSpec.
17810 static PartitionSpec
*
17811 transformPartitionSpec(Relation rel
, PartitionSpec
*partspec
)
17813 PartitionSpec
*newspec
;
17814 ParseState
*pstate
;
17815 ParseNamespaceItem
*nsitem
;
17818 newspec
= makeNode(PartitionSpec
);
17820 newspec
->strategy
= partspec
->strategy
;
17821 newspec
->partParams
= NIL
;
17822 newspec
->location
= partspec
->location
;
17824 /* Check valid number of columns for strategy */
17825 if (partspec
->strategy
== PARTITION_STRATEGY_LIST
&&
17826 list_length(partspec
->partParams
) != 1)
17828 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17829 errmsg("cannot use \"list\" partition strategy with more than one column")));
17832 * Create a dummy ParseState and insert the target relation as its sole
17833 * rangetable entry. We need a ParseState for transformExpr.
17835 pstate
= make_parsestate(NULL
);
17836 nsitem
= addRangeTableEntryForRelation(pstate
, rel
, AccessShareLock
,
17837 NULL
, false, true);
17838 addNSItemToQuery(pstate
, nsitem
, true, true, true);
17840 /* take care of any partition expressions */
17841 foreach(l
, partspec
->partParams
)
17843 PartitionElem
*pelem
= lfirst_node(PartitionElem
, l
);
17847 /* Copy, to avoid scribbling on the input */
17848 pelem
= copyObject(pelem
);
17850 /* Now do parse transformation of the expression */
17851 pelem
->expr
= transformExpr(pstate
, pelem
->expr
,
17852 EXPR_KIND_PARTITION_EXPRESSION
);
17854 /* we have to fix its collations too */
17855 assign_expr_collations(pstate
, pelem
->expr
);
17858 newspec
->partParams
= lappend(newspec
->partParams
, pelem
);
17865 * Compute per-partition-column information from a list of PartitionElems.
17866 * Expressions in the PartitionElems must be parse-analyzed already.
17869 ComputePartitionAttrs(ParseState
*pstate
, Relation rel
, List
*partParams
, AttrNumber
*partattrs
,
17870 List
**partexprs
, Oid
*partopclass
, Oid
*partcollation
,
17871 PartitionStrategy strategy
)
17878 foreach(lc
, partParams
)
17880 PartitionElem
*pelem
= lfirst_node(PartitionElem
, lc
);
17884 if (pelem
->name
!= NULL
)
17886 /* Simple attribute reference */
17887 HeapTuple atttuple
;
17888 Form_pg_attribute attform
;
17890 atttuple
= SearchSysCacheAttName(RelationGetRelid(rel
),
17892 if (!HeapTupleIsValid(atttuple
))
17894 (errcode(ERRCODE_UNDEFINED_COLUMN
),
17895 errmsg("column \"%s\" named in partition key does not exist",
17897 parser_errposition(pstate
, pelem
->location
)));
17898 attform
= (Form_pg_attribute
) GETSTRUCT(atttuple
);
17900 if (attform
->attnum
<= 0)
17902 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17903 errmsg("cannot use system column \"%s\" in partition key",
17905 parser_errposition(pstate
, pelem
->location
)));
17908 * Generated columns cannot work: They are computed after BEFORE
17909 * triggers, but partition routing is done before all triggers.
17911 if (attform
->attgenerated
)
17913 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17914 errmsg("cannot use generated column in partition key"),
17915 errdetail("Column \"%s\" is a generated column.",
17917 parser_errposition(pstate
, pelem
->location
)));
17919 partattrs
[attn
] = attform
->attnum
;
17920 atttype
= attform
->atttypid
;
17921 attcollation
= attform
->attcollation
;
17922 ReleaseSysCache(atttuple
);
17927 Node
*expr
= pelem
->expr
;
17928 char partattname
[16];
17930 Assert(expr
!= NULL
);
17931 atttype
= exprType(expr
);
17932 attcollation
= exprCollation(expr
);
17935 * The expression must be of a storable type (e.g., not RECORD).
17936 * The test is the same as for whether a table column is of a safe
17937 * type (which is why we needn't check for the non-expression
17940 snprintf(partattname
, sizeof(partattname
), "%d", attn
+ 1);
17941 CheckAttributeType(partattname
,
17942 atttype
, attcollation
,
17943 NIL
, CHKATYPE_IS_PARTKEY
);
17946 * Strip any top-level COLLATE clause. This ensures that we treat
17947 * "x COLLATE y" and "(x COLLATE y)" alike.
17949 while (IsA(expr
, CollateExpr
))
17950 expr
= (Node
*) ((CollateExpr
*) expr
)->arg
;
17952 if (IsA(expr
, Var
) &&
17953 ((Var
*) expr
)->varattno
> 0)
17956 * User wrote "(column)" or "(column COLLATE something)".
17957 * Treat it like simple attribute anyway.
17959 partattrs
[attn
] = ((Var
*) expr
)->varattno
;
17963 Bitmapset
*expr_attrs
= NULL
;
17966 partattrs
[attn
] = 0; /* marks the column as expression */
17967 *partexprs
= lappend(*partexprs
, expr
);
17970 * transformPartitionSpec() should have already rejected
17971 * subqueries, aggregates, window functions, and SRFs, based
17972 * on the EXPR_KIND_ for partition expressions.
17976 * Cannot allow system column references, since that would
17977 * make partition routing impossible: their values won't be
17978 * known yet when we need to do that.
17980 pull_varattnos(expr
, 1, &expr_attrs
);
17981 for (i
= FirstLowInvalidHeapAttributeNumber
; i
< 0; i
++)
17983 if (bms_is_member(i
- FirstLowInvalidHeapAttributeNumber
,
17986 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
17987 errmsg("partition key expressions cannot contain system column references")));
17991 * Generated columns cannot work: They are computed after
17992 * BEFORE triggers, but partition routing is done before all
17996 while ((i
= bms_next_member(expr_attrs
, i
)) >= 0)
17998 AttrNumber attno
= i
+ FirstLowInvalidHeapAttributeNumber
;
18001 TupleDescAttr(RelationGetDescr(rel
), attno
- 1)->attgenerated
)
18003 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18004 errmsg("cannot use generated column in partition key"),
18005 errdetail("Column \"%s\" is a generated column.",
18006 get_attname(RelationGetRelid(rel
), attno
, false)),
18007 parser_errposition(pstate
, pelem
->location
)));
18011 * Preprocess the expression before checking for mutability.
18012 * This is essential for the reasons described in
18013 * contain_mutable_functions_after_planning. However, we call
18014 * expression_planner for ourselves rather than using that
18015 * function, because if constant-folding reduces the
18016 * expression to a constant, we'd like to know that so we can
18019 * Like contain_mutable_functions_after_planning, assume that
18020 * expression_planner won't scribble on its input, so this
18021 * won't affect the partexprs entry we saved above.
18023 expr
= (Node
*) expression_planner((Expr
*) expr
);
18026 * Partition expressions cannot contain mutable functions,
18027 * because a given row must always map to the same partition
18028 * as long as there is no change in the partition boundary
18031 if (contain_mutable_functions(expr
))
18033 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18034 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18037 * While it is not exactly *wrong* for a partition expression
18038 * to be a constant, it seems better to reject such keys.
18040 if (IsA(expr
, Const
))
18042 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
18043 errmsg("cannot use constant expression as partition key")));
18048 * Apply collation override if any
18050 if (pelem
->collation
)
18051 attcollation
= get_collation_oid(pelem
->collation
, false);
18054 * Check we have a collation iff it's a collatable type. The only
18055 * expected failures here are (1) COLLATE applied to a noncollatable
18056 * type, or (2) partition expression had an unresolved collation. But
18057 * we might as well code this to be a complete consistency check.
18059 if (type_is_collatable(atttype
))
18061 if (!OidIsValid(attcollation
))
18063 (errcode(ERRCODE_INDETERMINATE_COLLATION
),
18064 errmsg("could not determine which collation to use for partition expression"),
18065 errhint("Use the COLLATE clause to set the collation explicitly.")));
18069 if (OidIsValid(attcollation
))
18071 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18072 errmsg("collations are not supported by type %s",
18073 format_type_be(atttype
))));
18076 partcollation
[attn
] = attcollation
;
18079 * Identify the appropriate operator class. For list and range
18080 * partitioning, we use a btree operator class; hash partitioning uses
18081 * a hash operator class.
18083 if (strategy
== PARTITION_STRATEGY_HASH
)
18084 am_oid
= HASH_AM_OID
;
18086 am_oid
= BTREE_AM_OID
;
18088 if (!pelem
->opclass
)
18090 partopclass
[attn
] = GetDefaultOpClass(atttype
, am_oid
);
18092 if (!OidIsValid(partopclass
[attn
]))
18094 if (strategy
== PARTITION_STRATEGY_HASH
)
18096 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18097 errmsg("data type %s has no default operator class for access method \"%s\"",
18098 format_type_be(atttype
), "hash"),
18099 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18102 (errcode(ERRCODE_UNDEFINED_OBJECT
),
18103 errmsg("data type %s has no default operator class for access method \"%s\"",
18104 format_type_be(atttype
), "btree"),
18105 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18109 partopclass
[attn
] = ResolveOpClass(pelem
->opclass
,
18111 am_oid
== HASH_AM_OID
? "hash" : "btree",
18119 * PartConstraintImpliedByRelConstraint
18120 * Do scanrel's existing constraints imply the partition constraint?
18122 * "Existing constraints" include its check constraints and column-level
18123 * not-null constraints. partConstraint describes the partition constraint,
18124 * in implicit-AND form.
18127 PartConstraintImpliedByRelConstraint(Relation scanrel
,
18128 List
*partConstraint
)
18130 List
*existConstraint
= NIL
;
18131 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18134 if (constr
&& constr
->has_not_null
)
18136 int natts
= scanrel
->rd_att
->natts
;
18138 for (i
= 1; i
<= natts
; i
++)
18140 Form_pg_attribute att
= TupleDescAttr(scanrel
->rd_att
, i
- 1);
18142 if (att
->attnotnull
&& !att
->attisdropped
)
18144 NullTest
*ntest
= makeNode(NullTest
);
18146 ntest
->arg
= (Expr
*) makeVar(1,
18152 ntest
->nulltesttype
= IS_NOT_NULL
;
18155 * argisrow=false is correct even for a composite column,
18156 * because attnotnull does not represent a SQL-spec IS NOT
18157 * NULL test in such a case, just IS DISTINCT FROM NULL.
18159 ntest
->argisrow
= false;
18160 ntest
->location
= -1;
18161 existConstraint
= lappend(existConstraint
, ntest
);
18166 return ConstraintImpliedByRelConstraint(scanrel
, partConstraint
, existConstraint
);
18170 * ConstraintImpliedByRelConstraint
18171 * Do scanrel's existing constraints imply the given constraint?
18173 * testConstraint is the constraint to validate. provenConstraint is a
18174 * caller-provided list of conditions which this function may assume
18175 * to be true. Both provenConstraint and testConstraint must be in
18176 * implicit-AND form, must only contain immutable clauses, and must
18177 * contain only Vars with varno = 1.
18180 ConstraintImpliedByRelConstraint(Relation scanrel
, List
*testConstraint
, List
*provenConstraint
)
18182 List
*existConstraint
= list_copy(provenConstraint
);
18183 TupleConstr
*constr
= RelationGetDescr(scanrel
)->constr
;
18187 num_check
= (constr
!= NULL
) ? constr
->num_check
: 0;
18188 for (i
= 0; i
< num_check
; i
++)
18193 * If this constraint hasn't been fully validated yet, we must ignore
18196 if (!constr
->check
[i
].ccvalid
)
18199 cexpr
= stringToNode(constr
->check
[i
].ccbin
);
18202 * Run each expression through const-simplification and
18203 * canonicalization. It is necessary, because we will be comparing it
18204 * to similarly-processed partition constraint expressions, and may
18205 * fail to detect valid matches without this.
18207 cexpr
= eval_const_expressions(NULL
, cexpr
);
18208 cexpr
= (Node
*) canonicalize_qual((Expr
*) cexpr
, true);
18210 existConstraint
= list_concat(existConstraint
,
18211 make_ands_implicit((Expr
*) cexpr
));
18215 * Try to make the proof. Since we are comparing CHECK constraints, we
18216 * need to use weak implication, i.e., we assume existConstraint is
18217 * not-false and try to prove the same for testConstraint.
18219 * Note that predicate_implied_by assumes its first argument is known
18220 * immutable. That should always be true for both NOT NULL and partition
18221 * constraints, so we don't test it here.
18223 return predicate_implied_by(testConstraint
, existConstraint
, true);
18227 * QueuePartitionConstraintValidation
18229 * Add an entry to wqueue to have the given partition constraint validated by
18230 * Phase 3, for the given relation, and all its children.
18232 * We first verify whether the given constraint is implied by pre-existing
18233 * relation constraints; if it is, there's no need to scan the table to
18234 * validate, so don't queue in that case.
18237 QueuePartitionConstraintValidation(List
**wqueue
, Relation scanrel
,
18238 List
*partConstraint
,
18239 bool validate_default
)
18242 * Based on the table's existing constraints, determine whether or not we
18243 * may skip scanning the table.
18245 if (PartConstraintImpliedByRelConstraint(scanrel
, partConstraint
))
18247 if (!validate_default
)
18249 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18250 RelationGetRelationName(scanrel
))));
18253 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18254 RelationGetRelationName(scanrel
))));
18259 * Constraints proved insufficient. For plain relations, queue a
18260 * validation item now; for partitioned tables, recurse to process each
18263 if (scanrel
->rd_rel
->relkind
== RELKIND_RELATION
)
18265 AlteredTableInfo
*tab
;
18267 /* Grab a work queue entry. */
18268 tab
= ATGetQueueEntry(wqueue
, scanrel
);
18269 Assert(tab
->partition_constraint
== NULL
);
18270 tab
->partition_constraint
= (Expr
*) linitial(partConstraint
);
18271 tab
->validate_default
= validate_default
;
18273 else if (scanrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18275 PartitionDesc partdesc
= RelationGetPartitionDesc(scanrel
, true);
18278 for (i
= 0; i
< partdesc
->nparts
; i
++)
18281 List
*thisPartConstraint
;
18284 * This is the minimum lock we need to prevent deadlocks.
18286 part_rel
= table_open(partdesc
->oids
[i
], AccessExclusiveLock
);
18289 * Adjust the constraint for scanrel so that it matches this
18290 * partition's attribute numbers.
18292 thisPartConstraint
=
18293 map_partition_varattnos(partConstraint
, 1,
18294 part_rel
, scanrel
);
18296 QueuePartitionConstraintValidation(wqueue
, part_rel
,
18297 thisPartConstraint
,
18299 table_close(part_rel
, NoLock
); /* keep lock till commit */
18305 * attachPartitionTable: attach a new partition to the partitioned table
18307 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
18308 * of an ALTER TABLE sequence.
18309 * rel: partitioned relation;
18310 * attachrel: relation of attached partition;
18311 * bound: bounds of attached relation.
18314 attachPartitionTable(List
**wqueue
, Relation rel
, Relation attachrel
, PartitionBoundSpec
*bound
)
18316 /* OK to create inheritance. Rest of the checks performed there */
18317 CreateInheritance(attachrel
, rel
, true);
18319 /* Update the pg_class entry. */
18320 StorePartitionBound(attachrel
, rel
, bound
);
18322 /* Ensure there exists a correct set of indexes in the partition. */
18323 AttachPartitionEnsureIndexes(wqueue
, rel
, attachrel
);
18326 CloneRowTriggersToPartition(rel
, attachrel
);
18329 * Clone foreign key constraints. Callee is responsible for setting up
18330 * for phase 3 constraint verification.
18332 CloneForeignKeyConstraints(wqueue
, rel
, attachrel
);
18336 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18338 * Return the address of the newly attached partition.
18340 static ObjectAddress
18341 ATExecAttachPartition(List
**wqueue
, Relation rel
, PartitionCmd
*cmd
,
18342 AlterTableUtilityContext
*context
)
18344 Relation attachrel
,
18346 List
*attachrel_children
;
18347 List
*partConstraint
;
18352 TupleDesc tupleDesc
;
18353 ObjectAddress address
;
18354 const char *trigger_name
;
18355 Oid defaultPartOid
;
18356 List
*partBoundConstraint
;
18357 ParseState
*pstate
= make_parsestate(NULL
);
18359 pstate
->p_sourcetext
= context
->queryString
;
18362 * We must lock the default partition if one exists, because attaching a
18363 * new partition will change its partition constraint.
18366 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
18367 if (OidIsValid(defaultPartOid
))
18368 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
18370 attachrel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
18373 * XXX I think it'd be a good idea to grab locks on all tables referenced
18374 * by FKs at this point also.
18378 * Must be owner of both parent and source table -- parent was checked by
18379 * ATSimplePermissions call in ATPrepCmd
18381 ATSimplePermissions(AT_AttachPartition
, attachrel
, ATT_TABLE
| ATT_FOREIGN_TABLE
);
18383 /* A partition can only have one parent */
18384 if (attachrel
->rd_rel
->relispartition
)
18386 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18387 errmsg("\"%s\" is already a partition",
18388 RelationGetRelationName(attachrel
))));
18390 if (OidIsValid(attachrel
->rd_rel
->reloftype
))
18392 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18393 errmsg("cannot attach a typed table as partition")));
18396 * Table being attached should not already be part of inheritance; either
18397 * as a child table...
18399 catalog
= table_open(InheritsRelationId
, AccessShareLock
);
18401 Anum_pg_inherits_inhrelid
,
18402 BTEqualStrategyNumber
, F_OIDEQ
,
18403 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
18404 scan
= systable_beginscan(catalog
, InheritsRelidSeqnoIndexId
, true,
18406 if (HeapTupleIsValid(systable_getnext(scan
)))
18408 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18409 errmsg("cannot attach inheritance child as partition")));
18410 systable_endscan(scan
);
18412 /* ...or as a parent table (except the case when it is partitioned) */
18414 Anum_pg_inherits_inhparent
,
18415 BTEqualStrategyNumber
, F_OIDEQ
,
18416 ObjectIdGetDatum(RelationGetRelid(attachrel
)));
18417 scan
= systable_beginscan(catalog
, InheritsParentIndexId
, true, NULL
,
18419 if (HeapTupleIsValid(systable_getnext(scan
)) &&
18420 attachrel
->rd_rel
->relkind
== RELKIND_RELATION
)
18422 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18423 errmsg("cannot attach inheritance parent as partition")));
18424 systable_endscan(scan
);
18425 table_close(catalog
, AccessShareLock
);
18428 * Prevent circularity by seeing if rel is a partition of attachrel. (In
18429 * particular, this disallows making a rel a partition of itself.)
18431 * We do that by checking if rel is a member of the list of attachrel's
18432 * partitions provided the latter is partitioned at all. We want to avoid
18433 * having to construct this list again, so we request the strongest lock
18434 * on all partitions. We need the strongest lock, because we may decide
18435 * to scan them if we find out that the table being attached (or its leaf
18436 * partitions) may contain rows that violate the partition constraint. If
18437 * the table has a constraint that would prevent such rows, which by
18438 * definition is present in all the partitions, we need not scan the
18439 * table, nor its partitions. But we cannot risk a deadlock by taking a
18440 * weaker lock now and the stronger one only when needed.
18442 attachrel_children
= find_all_inheritors(RelationGetRelid(attachrel
),
18443 AccessExclusiveLock
, NULL
);
18444 if (list_member_oid(attachrel_children
, RelationGetRelid(rel
)))
18446 (errcode(ERRCODE_DUPLICATE_TABLE
),
18447 errmsg("circular inheritance not allowed"),
18448 errdetail("\"%s\" is already a child of \"%s\".",
18449 RelationGetRelationName(rel
),
18450 RelationGetRelationName(attachrel
))));
18452 /* If the parent is permanent, so must be all of its partitions. */
18453 if (rel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
18454 attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
18456 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18457 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18458 RelationGetRelationName(rel
))));
18460 /* Temp parent cannot have a partition that is itself not a temp */
18461 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18462 attachrel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
)
18464 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18465 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18466 RelationGetRelationName(rel
))));
18468 /* If the parent is temp, it must belong to this session */
18469 if (rel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18470 !rel
->rd_islocaltemp
)
18472 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18473 errmsg("cannot attach as partition of temporary relation of another session")));
18475 /* Ditto for the partition */
18476 if (attachrel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
18477 !attachrel
->rd_islocaltemp
)
18479 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18480 errmsg("cannot attach temporary relation of another session as partition")));
18483 * Check if attachrel has any identity columns or any columns that aren't
18486 tupleDesc
= RelationGetDescr(attachrel
);
18487 natts
= tupleDesc
->natts
;
18488 for (attno
= 1; attno
<= natts
; attno
++)
18490 Form_pg_attribute attribute
= TupleDescAttr(tupleDesc
, attno
- 1);
18491 char *attributeName
= NameStr(attribute
->attname
);
18493 /* Ignore dropped */
18494 if (attribute
->attisdropped
)
18497 if (attribute
->attidentity
)
18499 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
18500 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
18501 RelationGetRelationName(attachrel
), attributeName
),
18502 errdetail("The new partition may not contain an identity column."));
18504 /* Try to find the column in parent (matching on column name) */
18505 if (!SearchSysCacheExists2(ATTNAME
,
18506 ObjectIdGetDatum(RelationGetRelid(rel
)),
18507 CStringGetDatum(attributeName
)))
18509 (errcode(ERRCODE_DATATYPE_MISMATCH
),
18510 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18511 RelationGetRelationName(attachrel
), attributeName
,
18512 RelationGetRelationName(rel
)),
18513 errdetail("The new partition may contain only the columns present in parent.")));
18517 * If child_rel has row-level triggers with transition tables, we
18518 * currently don't allow it to become a partition. See also prohibitions
18519 * in ATExecAddInherit() and CreateTrigger().
18521 trigger_name
= FindTriggerIncompatibleWithInheritance(attachrel
->trigdesc
);
18522 if (trigger_name
!= NULL
)
18524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
18525 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18526 trigger_name
, RelationGetRelationName(attachrel
)),
18527 errdetail("ROW triggers with transition tables are not supported on partitions.")));
18530 * Check that the new partition's bound is valid and does not overlap any
18531 * of existing partitions of the parent - note that it does not return on
18534 check_new_partition_bound(RelationGetRelationName(attachrel
), rel
,
18535 cmd
->bound
, pstate
);
18537 /* Attach a new partition to the partitioned table. */
18538 attachPartitionTable(wqueue
, rel
, attachrel
, cmd
->bound
);
18541 * Generate partition constraint from the partition bound specification.
18542 * If the parent itself is a partition, make sure to include its
18543 * constraint as well.
18545 partBoundConstraint
= get_qual_from_partbound(rel
, cmd
->bound
);
18546 partConstraint
= list_concat(partBoundConstraint
,
18547 RelationGetPartitionQual(rel
));
18549 /* Skip validation if there are no constraints to validate. */
18550 if (partConstraint
)
18553 * Run the partition quals through const-simplification similar to
18554 * check constraints. We skip canonicalize_qual, though, because
18555 * partition quals should be in canonical form already.
18558 (List
*) eval_const_expressions(NULL
,
18559 (Node
*) partConstraint
);
18561 /* XXX this sure looks wrong */
18562 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
18565 * Adjust the generated constraint to match this partition's attribute
18568 partConstraint
= map_partition_varattnos(partConstraint
, 1, attachrel
,
18571 /* Validate partition constraints against the table being attached. */
18572 QueuePartitionConstraintValidation(wqueue
, attachrel
, partConstraint
,
18577 * If we're attaching a partition other than the default partition and a
18578 * default one exists, then that partition's partition constraint changes,
18579 * so add an entry to the work queue to validate it, too. (We must not do
18580 * this when the partition being attached is the default one; we already
18583 if (OidIsValid(defaultPartOid
))
18585 Relation defaultrel
;
18586 List
*defPartConstraint
;
18588 Assert(!cmd
->bound
->is_default
);
18590 /* we already hold a lock on the default partition */
18591 defaultrel
= table_open(defaultPartOid
, NoLock
);
18592 defPartConstraint
=
18593 get_proposed_default_constraint(partBoundConstraint
);
18596 * Map the Vars in the constraint expression from rel's attnos to
18599 defPartConstraint
=
18600 map_partition_varattnos(defPartConstraint
,
18601 1, defaultrel
, rel
);
18602 QueuePartitionConstraintValidation(wqueue
, defaultrel
,
18603 defPartConstraint
, true);
18605 /* keep our lock until commit. */
18606 table_close(defaultrel
, NoLock
);
18609 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(attachrel
));
18612 * If the partition we just attached is partitioned itself, invalidate
18613 * relcache for all descendent partitions too to ensure that their
18614 * rd_partcheck expression trees are rebuilt; partitions already locked at
18615 * the beginning of this function.
18617 if (attachrel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
18621 foreach(l
, attachrel_children
)
18623 CacheInvalidateRelcacheByRelid(lfirst_oid(l
));
18627 /* keep our lock until commit */
18628 table_close(attachrel
, NoLock
);
18634 * AttachPartitionEnsureIndexes
18635 * subroutine for ATExecAttachPartition to create/match indexes
18637 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18638 * PARTITION: every partition must have an index attached to each index on the
18639 * partitioned table.
18642 AttachPartitionEnsureIndexes(List
**wqueue
, Relation rel
, Relation attachrel
)
18645 List
*attachRelIdxs
;
18646 Relation
*attachrelIdxRels
;
18647 IndexInfo
**attachInfos
;
18650 MemoryContext oldcxt
;
18652 cxt
= AllocSetContextCreate(CurrentMemoryContext
,
18653 "AttachPartitionEnsureIndexes",
18654 ALLOCSET_DEFAULT_SIZES
);
18655 oldcxt
= MemoryContextSwitchTo(cxt
);
18657 idxes
= RelationGetIndexList(rel
);
18658 attachRelIdxs
= RelationGetIndexList(attachrel
);
18659 attachrelIdxRels
= palloc(sizeof(Relation
) * list_length(attachRelIdxs
));
18660 attachInfos
= palloc(sizeof(IndexInfo
*) * list_length(attachRelIdxs
));
18662 /* Build arrays of all existing indexes and their IndexInfos */
18663 foreach_oid(cldIdxId
, attachRelIdxs
)
18665 int i
= foreach_current_index(cldIdxId
);
18667 attachrelIdxRels
[i
] = index_open(cldIdxId
, AccessShareLock
);
18668 attachInfos
[i
] = BuildIndexInfo(attachrelIdxRels
[i
]);
18672 * If we're attaching a foreign table, we must fail if any of the indexes
18673 * is a constraint index; otherwise, there's nothing to do here. Do this
18674 * before starting work, to avoid wasting the effort of building a few
18675 * non-unique indexes before coming across a unique one.
18677 if (attachrel
->rd_rel
->relkind
== RELKIND_FOREIGN_TABLE
)
18679 foreach(cell
, idxes
)
18681 Oid idx
= lfirst_oid(cell
);
18682 Relation idxRel
= index_open(idx
, AccessShareLock
);
18684 if (idxRel
->rd_index
->indisunique
||
18685 idxRel
->rd_index
->indisprimary
)
18687 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
18688 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18689 RelationGetRelationName(attachrel
),
18690 RelationGetRelationName(rel
)),
18691 errdetail("Partitioned table \"%s\" contains unique indexes.",
18692 RelationGetRelationName(rel
))));
18693 index_close(idxRel
, AccessShareLock
);
18700 * For each index on the partitioned table, find a matching one in the
18701 * partition-to-be; if one is not found, create one.
18703 foreach(cell
, idxes
)
18705 Oid idx
= lfirst_oid(cell
);
18706 Relation idxRel
= index_open(idx
, AccessShareLock
);
18709 bool found
= false;
18713 * Ignore indexes in the partitioned table other than partitioned
18716 if (idxRel
->rd_rel
->relkind
!= RELKIND_PARTITIONED_INDEX
)
18718 index_close(idxRel
, AccessShareLock
);
18722 /* construct an indexinfo to compare existing indexes against */
18723 info
= BuildIndexInfo(idxRel
);
18724 attmap
= build_attrmap_by_name(RelationGetDescr(attachrel
),
18725 RelationGetDescr(rel
),
18727 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
), idx
);
18730 * Scan the list of existing indexes in the partition-to-be, and mark
18731 * the first matching, valid, unattached one we find, if any, as
18732 * partition of the parent index. If we find one, we're done.
18734 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
18736 Oid cldIdxId
= RelationGetRelid(attachrelIdxRels
[i
]);
18737 Oid cldConstrOid
= InvalidOid
;
18739 /* does this index have a parent? if so, can't use it */
18740 if (attachrelIdxRels
[i
]->rd_rel
->relispartition
)
18743 /* If this index is invalid, can't use it */
18744 if (!attachrelIdxRels
[i
]->rd_index
->indisvalid
)
18747 if (CompareIndexInfo(attachInfos
[i
], info
,
18748 attachrelIdxRels
[i
]->rd_indcollation
,
18749 idxRel
->rd_indcollation
,
18750 attachrelIdxRels
[i
]->rd_opfamily
,
18751 idxRel
->rd_opfamily
,
18755 * If this index is being created in the parent because of a
18756 * constraint, then the child needs to have a constraint also,
18757 * so look for one. If there is no such constraint, this
18758 * index is no good, so keep looking.
18760 if (OidIsValid(constraintOid
))
18763 get_relation_idx_constraint_oid(RelationGetRelid(attachrel
),
18766 if (!OidIsValid(cldConstrOid
))
18769 /* Ensure they're both the same type of constraint */
18770 if (get_constraint_type(constraintOid
) !=
18771 get_constraint_type(cldConstrOid
))
18776 IndexSetParentIndex(attachrelIdxRels
[i
], idx
);
18777 if (OidIsValid(constraintOid
))
18778 ConstraintSetParentConstraint(cldConstrOid
, constraintOid
,
18779 RelationGetRelid(attachrel
));
18782 CommandCounterIncrement();
18788 * If no suitable index was found in the partition-to-be, create one
18796 stmt
= generateClonedIndexStmt(NULL
,
18799 DefineIndex(RelationGetRelid(attachrel
), stmt
, InvalidOid
,
18800 RelationGetRelid(idxRel
),
18803 true, false, false, false, false);
18806 index_close(idxRel
, AccessShareLock
);
18811 for (int i
= 0; i
< list_length(attachRelIdxs
); i
++)
18812 index_close(attachrelIdxRels
[i
], AccessShareLock
);
18813 MemoryContextSwitchTo(oldcxt
);
18814 MemoryContextDelete(cxt
);
18818 * CloneRowTriggersToPartition
18819 * subroutine for ATExecAttachPartition/DefineRelation to create row
18820 * triggers on partitions
18823 CloneRowTriggersToPartition(Relation parent
, Relation partition
)
18825 Relation pg_trigger
;
18829 MemoryContext perTupCxt
;
18831 ScanKeyInit(&key
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
18832 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(parent
)));
18833 pg_trigger
= table_open(TriggerRelationId
, RowExclusiveLock
);
18834 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
,
18835 true, NULL
, 1, &key
);
18837 perTupCxt
= AllocSetContextCreate(CurrentMemoryContext
,
18838 "clone trig", ALLOCSET_SMALL_SIZES
);
18840 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
18842 Form_pg_trigger trigForm
= (Form_pg_trigger
) GETSTRUCT(tuple
);
18843 CreateTrigStmt
*trigStmt
;
18848 List
*trigargs
= NIL
;
18849 MemoryContext oldcxt
;
18852 * Ignore statement-level triggers; those are not cloned.
18854 if (!TRIGGER_FOR_ROW(trigForm
->tgtype
))
18858 * Don't clone internal triggers, because the constraint cloning code
18861 if (trigForm
->tgisinternal
)
18865 * Complain if we find an unexpected trigger type.
18867 if (!TRIGGER_FOR_BEFORE(trigForm
->tgtype
) &&
18868 !TRIGGER_FOR_AFTER(trigForm
->tgtype
))
18869 elog(ERROR
, "unexpected trigger \"%s\" found",
18870 NameStr(trigForm
->tgname
));
18872 /* Use short-lived context for CREATE TRIGGER */
18873 oldcxt
= MemoryContextSwitchTo(perTupCxt
);
18876 * If there is a WHEN clause, generate a 'cooked' version of it that's
18877 * appropriate for the partition.
18879 value
= heap_getattr(tuple
, Anum_pg_trigger_tgqual
,
18880 RelationGetDescr(pg_trigger
), &isnull
);
18883 qual
= stringToNode(TextDatumGetCString(value
));
18884 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_OLD_VARNO
,
18885 partition
, parent
);
18886 qual
= (Node
*) map_partition_varattnos((List
*) qual
, PRS2_NEW_VARNO
,
18887 partition
, parent
);
18891 * If there is a column list, transform it to a list of column names.
18892 * Note we don't need to map this list in any way ...
18894 if (trigForm
->tgattr
.dim1
> 0)
18898 for (i
= 0; i
< trigForm
->tgattr
.dim1
; i
++)
18900 Form_pg_attribute col
;
18902 col
= TupleDescAttr(parent
->rd_att
,
18903 trigForm
->tgattr
.values
[i
] - 1);
18904 cols
= lappend(cols
,
18905 makeString(pstrdup(NameStr(col
->attname
))));
18909 /* Reconstruct trigger arguments list. */
18910 if (trigForm
->tgnargs
> 0)
18914 value
= heap_getattr(tuple
, Anum_pg_trigger_tgargs
,
18915 RelationGetDescr(pg_trigger
), &isnull
);
18917 elog(ERROR
, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18918 NameStr(trigForm
->tgname
), RelationGetRelationName(partition
));
18920 p
= (char *) VARDATA_ANY(DatumGetByteaPP(value
));
18922 for (int i
= 0; i
< trigForm
->tgnargs
; i
++)
18924 trigargs
= lappend(trigargs
, makeString(pstrdup(p
)));
18925 p
+= strlen(p
) + 1;
18929 trigStmt
= makeNode(CreateTrigStmt
);
18930 trigStmt
->replace
= false;
18931 trigStmt
->isconstraint
= OidIsValid(trigForm
->tgconstraint
);
18932 trigStmt
->trigname
= NameStr(trigForm
->tgname
);
18933 trigStmt
->relation
= NULL
;
18934 trigStmt
->funcname
= NULL
; /* passed separately */
18935 trigStmt
->args
= trigargs
;
18936 trigStmt
->row
= true;
18937 trigStmt
->timing
= trigForm
->tgtype
& TRIGGER_TYPE_TIMING_MASK
;
18938 trigStmt
->events
= trigForm
->tgtype
& TRIGGER_TYPE_EVENT_MASK
;
18939 trigStmt
->columns
= cols
;
18940 trigStmt
->whenClause
= NULL
; /* passed separately */
18941 trigStmt
->transitionRels
= NIL
; /* not supported at present */
18942 trigStmt
->deferrable
= trigForm
->tgdeferrable
;
18943 trigStmt
->initdeferred
= trigForm
->tginitdeferred
;
18944 trigStmt
->constrrel
= NULL
; /* passed separately */
18946 CreateTriggerFiringOn(trigStmt
, NULL
, RelationGetRelid(partition
),
18947 trigForm
->tgconstrrelid
, InvalidOid
, InvalidOid
,
18948 trigForm
->tgfoid
, trigForm
->oid
, qual
,
18949 false, true, trigForm
->tgenabled
);
18951 MemoryContextSwitchTo(oldcxt
);
18952 MemoryContextReset(perTupCxt
);
18955 MemoryContextDelete(perTupCxt
);
18957 systable_endscan(scan
);
18958 table_close(pg_trigger
, RowExclusiveLock
);
18962 * ALTER TABLE DETACH PARTITION
18964 * Return the address of the relation that is no longer a partition of rel.
18966 * If concurrent mode is requested, we run in two transactions. A side-
18967 * effect is that this command cannot run in a multi-part ALTER TABLE.
18968 * Currently, that's enforced by the grammar.
18970 * The strategy for concurrency is to first modify the partition's
18971 * pg_inherit catalog row to make it visible to everyone that the
18972 * partition is detached, lock the partition against writes, and commit
18973 * the transaction; anyone who requests the partition descriptor from
18974 * that point onwards has to ignore such a partition. In a second
18975 * transaction, we wait until all transactions that could have seen the
18976 * partition as attached are gone, then we remove the rest of partition
18977 * metadata (pg_inherits and pg_class.relpartbounds).
18979 static ObjectAddress
18980 ATExecDetachPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
18981 RangeVar
*name
, bool concurrent
)
18984 ObjectAddress address
;
18985 Oid defaultPartOid
;
18988 * We must lock the default partition, because detaching this partition
18989 * will change its partition constraint.
18992 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
18993 if (OidIsValid(defaultPartOid
))
18996 * Concurrent detaching when a default partition exists is not
18997 * supported. The main problem is that the default partition
18998 * constraint would change. And there's a definitional problem: what
18999 * should happen to the tuples that are being inserted that belong to
19000 * the partition being detached? Putting them on the partition being
19001 * detached would be wrong, since they'd become "lost" after the
19002 * detaching completes but we cannot put them in the default partition
19003 * either until we alter its partition constraint.
19005 * I think we could solve this problem if we effected the constraint
19006 * change before committing the first transaction. But the lock would
19007 * have to remain AEL and it would cause concurrent query planning to
19008 * be blocked, so changing it that way would be even worse.
19012 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19013 errmsg("cannot detach partitions concurrently when a default partition exists")));
19014 LockRelationOid(defaultPartOid
, AccessExclusiveLock
);
19018 * In concurrent mode, the partition is locked with share-update-exclusive
19019 * in the first transaction. This allows concurrent transactions to be
19020 * doing DML to the partition.
19022 partRel
= table_openrv(name
, concurrent
? ShareUpdateExclusiveLock
:
19023 AccessExclusiveLock
);
19026 * Check inheritance conditions and either delete the pg_inherits row (in
19027 * non-concurrent mode) or just set the inhdetachpending flag.
19030 RemoveInheritance(partRel
, rel
, false);
19032 MarkInheritDetached(partRel
, rel
);
19035 * Ensure that foreign keys still hold after this detach. This keeps
19036 * locks on the referencing tables, which prevents concurrent transactions
19037 * from adding rows that we wouldn't see. For this to work in concurrent
19038 * mode, it is critical that the partition appears as no longer attached
19039 * for the RI queries as soon as the first transaction commits.
19041 ATDetachCheckNoForeignKeyRefs(partRel
);
19044 * Concurrent mode has to work harder; first we add a new constraint to
19045 * the partition that matches the partition constraint. Then we close our
19046 * existing transaction, and in a new one wait for all processes to catch
19047 * up on the catalog updates we've done so far; at that point we can
19048 * complete the operation.
19055 char *parentrelname
;
19059 * Add a new constraint to the partition being detached, which
19060 * supplants the partition constraint (unless there is one already).
19062 DetachAddConstraintIfNeeded(wqueue
, partRel
);
19065 * We're almost done now; the only traces that remain are the
19066 * pg_inherits tuple and the partition's relpartbounds. Before we can
19067 * remove those, we need to wait until all transactions that know that
19068 * this is a partition are gone.
19072 * Remember relation OIDs to re-acquire them later; and relation names
19073 * too, for error messages if something is dropped in between.
19075 partrelid
= RelationGetRelid(partRel
);
19076 parentrelid
= RelationGetRelid(rel
);
19077 parentrelname
= MemoryContextStrdup(PortalContext
,
19078 RelationGetRelationName(rel
));
19079 partrelname
= MemoryContextStrdup(PortalContext
,
19080 RelationGetRelationName(partRel
));
19082 /* Invalidate relcache entries for the parent -- must be before close */
19083 CacheInvalidateRelcache(rel
);
19085 table_close(partRel
, NoLock
);
19086 table_close(rel
, NoLock
);
19089 /* Make updated catalog entry visible */
19090 PopActiveSnapshot();
19091 CommitTransactionCommand();
19093 StartTransactionCommand();
19096 * Now wait. This ensures that all queries that were planned
19097 * including the partition are finished before we remove the rest of
19098 * catalog entries. We don't need or indeed want to acquire this
19099 * lock, though -- that would block later queries.
19101 * We don't need to concern ourselves with waiting for a lock on the
19102 * partition itself, since we will acquire AccessExclusiveLock below.
19104 SET_LOCKTAG_RELATION(tag
, MyDatabaseId
, parentrelid
);
19105 WaitForLockersMultiple(list_make1(&tag
), AccessExclusiveLock
, false);
19108 * Now acquire locks in both relations again. Note they may have been
19109 * removed in the meantime, so care is required.
19111 rel
= try_relation_open(parentrelid
, ShareUpdateExclusiveLock
);
19112 partRel
= try_relation_open(partrelid
, AccessExclusiveLock
);
19114 /* If the relations aren't there, something bad happened; bail out */
19117 if (partRel
!= NULL
) /* shouldn't happen */
19118 elog(WARNING
, "dangling partition \"%s\" remains, can't fix",
19121 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19122 errmsg("partitioned table \"%s\" was removed concurrently",
19125 if (partRel
== NULL
)
19127 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19128 errmsg("partition \"%s\" was removed concurrently", partrelname
)));
19133 /* Do the final part of detaching */
19134 DetachPartitionFinalize(rel
, partRel
, concurrent
, defaultPartOid
);
19136 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19138 /* keep our lock until commit */
19139 table_close(partRel
, NoLock
);
19145 * Second part of ALTER TABLE .. DETACH.
19147 * This is separate so that it can be run independently when the second
19148 * transaction of the concurrent algorithm fails (crash or abort).
19151 DetachPartitionFinalize(Relation rel
, Relation partRel
, bool concurrent
,
19152 Oid defaultPartOid
)
19158 Datum new_val
[Natts_pg_class
];
19159 bool new_null
[Natts_pg_class
],
19160 new_repl
[Natts_pg_class
];
19163 Relation trigrel
= NULL
;
19168 * We can remove the pg_inherits row now. (In the non-concurrent case,
19169 * this was already done).
19171 RemoveInheritance(partRel
, rel
, true);
19174 /* Drop any triggers that were cloned on creation/attach. */
19175 DropClonedTriggersFromPartition(RelationGetRelid(partRel
));
19178 * Detach any foreign keys that are inherited. This includes creating
19179 * additional action triggers.
19181 fks
= copyObject(RelationGetFKeyList(partRel
));
19183 trigrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19186 ForeignKeyCacheInfo
*fk
= lfirst(cell
);
19188 Form_pg_constraint conform
;
19189 Constraint
*fkconstraint
;
19190 Oid insertTriggerOid
,
19193 contup
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(fk
->conoid
));
19194 if (!HeapTupleIsValid(contup
))
19195 elog(ERROR
, "cache lookup failed for constraint %u", fk
->conoid
);
19196 conform
= (Form_pg_constraint
) GETSTRUCT(contup
);
19198 /* consider only the inherited foreign keys */
19199 if (conform
->contype
!= CONSTRAINT_FOREIGN
||
19200 !OidIsValid(conform
->conparentid
))
19202 ReleaseSysCache(contup
);
19206 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19207 ConstraintSetParentConstraint(fk
->conoid
, InvalidOid
, InvalidOid
);
19210 * Also, look up the partition's "check" triggers corresponding to the
19211 * constraint being detached and detach them from the parent triggers.
19213 GetForeignKeyCheckTriggers(trigrel
,
19214 fk
->conoid
, fk
->confrelid
, fk
->conrelid
,
19215 &insertTriggerOid
, &updateTriggerOid
);
19216 Assert(OidIsValid(insertTriggerOid
));
19217 TriggerSetParentTrigger(trigrel
, insertTriggerOid
, InvalidOid
,
19218 RelationGetRelid(partRel
));
19219 Assert(OidIsValid(updateTriggerOid
));
19220 TriggerSetParentTrigger(trigrel
, updateTriggerOid
, InvalidOid
,
19221 RelationGetRelid(partRel
));
19224 * Make the action triggers on the referenced relation. When this was
19225 * a partition the action triggers pointed to the parent rel (they
19226 * still do), but now we need separate ones of our own.
19228 fkconstraint
= makeNode(Constraint
);
19229 fkconstraint
->contype
= CONSTRAINT_FOREIGN
;
19230 fkconstraint
->conname
= pstrdup(NameStr(conform
->conname
));
19231 fkconstraint
->deferrable
= conform
->condeferrable
;
19232 fkconstraint
->initdeferred
= conform
->condeferred
;
19233 fkconstraint
->location
= -1;
19234 fkconstraint
->pktable
= NULL
;
19235 fkconstraint
->fk_attrs
= NIL
;
19236 fkconstraint
->pk_attrs
= NIL
;
19237 fkconstraint
->fk_matchtype
= conform
->confmatchtype
;
19238 fkconstraint
->fk_upd_action
= conform
->confupdtype
;
19239 fkconstraint
->fk_del_action
= conform
->confdeltype
;
19240 fkconstraint
->fk_del_set_cols
= NIL
;
19241 fkconstraint
->old_conpfeqop
= NIL
;
19242 fkconstraint
->old_pktable_oid
= InvalidOid
;
19243 fkconstraint
->skip_validation
= false;
19244 fkconstraint
->initially_valid
= true;
19246 createForeignKeyActionTriggers(partRel
, conform
->confrelid
,
19247 fkconstraint
, fk
->conoid
,
19249 InvalidOid
, InvalidOid
,
19252 ReleaseSysCache(contup
);
19254 list_free_deep(fks
);
19256 table_close(trigrel
, RowExclusiveLock
);
19259 * Any sub-constraints that are in the referenced-side of a larger
19260 * constraint have to be removed. This partition is no longer part of the
19261 * key space of the constraint.
19263 foreach(cell
, GetParentedForeignKeyRefs(partRel
))
19265 Oid constrOid
= lfirst_oid(cell
);
19266 ObjectAddress constraint
;
19268 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
19269 deleteDependencyRecordsForClass(ConstraintRelationId
,
19271 ConstraintRelationId
,
19272 DEPENDENCY_INTERNAL
);
19273 CommandCounterIncrement();
19275 ObjectAddressSet(constraint
, ConstraintRelationId
, constrOid
);
19276 performDeletion(&constraint
, DROP_RESTRICT
, 0);
19279 /* Now we can detach indexes */
19280 indexes
= RelationGetIndexList(partRel
);
19281 foreach(cell
, indexes
)
19283 Oid idxid
= lfirst_oid(cell
);
19287 Oid parentConstrOid
;
19289 if (!has_superclass(idxid
))
19292 parentidx
= get_partition_parent(idxid
, false);
19293 Assert((IndexGetRelation(parentidx
, false) == RelationGetRelid(rel
)));
19295 idx
= index_open(idxid
, AccessExclusiveLock
);
19296 IndexSetParentIndex(idx
, InvalidOid
);
19299 * If there's a constraint associated with the index, detach it too.
19300 * Careful: it is possible for a constraint index in a partition to be
19301 * the child of a non-constraint index, so verify whether the parent
19302 * index does actually have a constraint.
19304 constrOid
= get_relation_idx_constraint_oid(RelationGetRelid(partRel
),
19306 parentConstrOid
= get_relation_idx_constraint_oid(RelationGetRelid(rel
),
19308 if (OidIsValid(parentConstrOid
) && OidIsValid(constrOid
))
19309 ConstraintSetParentConstraint(constrOid
, InvalidOid
, InvalidOid
);
19311 index_close(idx
, NoLock
);
19314 /* Update pg_class tuple */
19315 classRel
= table_open(RelationRelationId
, RowExclusiveLock
);
19316 tuple
= SearchSysCacheCopy1(RELOID
,
19317 ObjectIdGetDatum(RelationGetRelid(partRel
)));
19318 if (!HeapTupleIsValid(tuple
))
19319 elog(ERROR
, "cache lookup failed for relation %u",
19320 RelationGetRelid(partRel
));
19321 Assert(((Form_pg_class
) GETSTRUCT(tuple
))->relispartition
);
19323 /* Clear relpartbound and reset relispartition */
19324 memset(new_val
, 0, sizeof(new_val
));
19325 memset(new_null
, false, sizeof(new_null
));
19326 memset(new_repl
, false, sizeof(new_repl
));
19327 new_val
[Anum_pg_class_relpartbound
- 1] = (Datum
) 0;
19328 new_null
[Anum_pg_class_relpartbound
- 1] = true;
19329 new_repl
[Anum_pg_class_relpartbound
- 1] = true;
19330 newtuple
= heap_modify_tuple(tuple
, RelationGetDescr(classRel
),
19331 new_val
, new_null
, new_repl
);
19333 ((Form_pg_class
) GETSTRUCT(newtuple
))->relispartition
= false;
19334 CatalogTupleUpdate(classRel
, &newtuple
->t_self
, newtuple
);
19335 heap_freetuple(newtuple
);
19336 table_close(classRel
, RowExclusiveLock
);
19339 * Drop identity property from all identity columns of partition.
19341 for (int attno
= 0; attno
< RelationGetNumberOfAttributes(partRel
); attno
++)
19343 Form_pg_attribute attr
= TupleDescAttr(partRel
->rd_att
, attno
);
19345 if (!attr
->attisdropped
&& attr
->attidentity
)
19346 ATExecDropIdentity(partRel
, NameStr(attr
->attname
), false,
19347 AccessExclusiveLock
, true, true);
19350 if (OidIsValid(defaultPartOid
))
19353 * If the relation being detached is the default partition itself,
19354 * remove it from the parent's pg_partitioned_table entry.
19356 * If not, we must invalidate default partition's relcache entry, as
19357 * in StorePartitionBound: its partition constraint depends on every
19358 * other partition's partition constraint.
19360 if (RelationGetRelid(partRel
) == defaultPartOid
)
19361 update_default_partition_oid(RelationGetRelid(rel
), InvalidOid
);
19363 CacheInvalidateRelcacheByRelid(defaultPartOid
);
19367 * Invalidate the parent's relcache so that the partition is no longer
19368 * included in its partition descriptor.
19370 CacheInvalidateRelcache(rel
);
19373 * If the partition we just detached is partitioned itself, invalidate
19374 * relcache for all descendent partitions too to ensure that their
19375 * rd_partcheck expression trees are rebuilt; must lock partitions before
19376 * doing so, using the same lockmode as what partRel has been locked with
19379 if (partRel
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
19383 children
= find_all_inheritors(RelationGetRelid(partRel
),
19384 AccessExclusiveLock
, NULL
);
19385 foreach(cell
, children
)
19387 CacheInvalidateRelcacheByRelid(lfirst_oid(cell
));
19393 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19395 * To use when a DETACH PARTITION command previously did not run to
19396 * completion; this completes the detaching process.
19398 static ObjectAddress
19399 ATExecDetachPartitionFinalize(Relation rel
, RangeVar
*name
)
19402 ObjectAddress address
;
19403 Snapshot snap
= GetActiveSnapshot();
19405 partRel
= table_openrv(name
, AccessExclusiveLock
);
19408 * Wait until existing snapshots are gone. This is important if the
19409 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19410 * user could immediately run DETACH FINALIZE without actually waiting for
19411 * existing transactions. We must not complete the detach action until
19412 * all such queries are complete (otherwise we would present them with an
19413 * inconsistent view of catalogs).
19415 WaitForOlderSnapshots(snap
->xmin
, false);
19417 DetachPartitionFinalize(rel
, partRel
, true, InvalidOid
);
19419 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partRel
));
19421 table_close(partRel
, NoLock
);
19427 * DetachAddConstraintIfNeeded
19428 * Subroutine for ATExecDetachPartition. Create a constraint that
19429 * takes the place of the partition constraint, but avoid creating
19430 * a dupe if an constraint already exists which implies the needed
19434 DetachAddConstraintIfNeeded(List
**wqueue
, Relation partRel
)
19436 List
*constraintExpr
;
19438 constraintExpr
= RelationGetPartitionQual(partRel
);
19439 constraintExpr
= (List
*) eval_const_expressions(NULL
, (Node
*) constraintExpr
);
19442 * Avoid adding a new constraint if the needed constraint is implied by an
19443 * existing constraint
19445 if (!PartConstraintImpliedByRelConstraint(partRel
, constraintExpr
))
19447 AlteredTableInfo
*tab
;
19450 tab
= ATGetQueueEntry(wqueue
, partRel
);
19452 /* Add constraint on partition, equivalent to the partition constraint */
19453 n
= makeNode(Constraint
);
19454 n
->contype
= CONSTR_CHECK
;
19457 n
->is_no_inherit
= false;
19458 n
->raw_expr
= NULL
;
19459 n
->cooked_expr
= nodeToString(make_ands_explicit(constraintExpr
));
19460 n
->initially_valid
= true;
19461 n
->skip_validation
= true;
19462 /* It's a re-add, since it nominally already exists */
19463 ATAddCheckConstraint(wqueue
, tab
, partRel
, n
,
19464 true, false, true, ShareUpdateExclusiveLock
);
19469 * DropClonedTriggersFromPartition
19470 * subroutine for ATExecDetachPartition to remove any triggers that were
19471 * cloned to the partition when it was created-as-partition or attached.
19472 * This undoes what CloneRowTriggersToPartition did.
19475 DropClonedTriggersFromPartition(Oid partitionId
)
19481 ObjectAddresses
*objects
;
19483 objects
= new_object_addresses();
19486 * Scan pg_trigger to search for all triggers on this rel.
19488 ScanKeyInit(&skey
, Anum_pg_trigger_tgrelid
, BTEqualStrategyNumber
,
19489 F_OIDEQ
, ObjectIdGetDatum(partitionId
));
19490 tgrel
= table_open(TriggerRelationId
, RowExclusiveLock
);
19491 scan
= systable_beginscan(tgrel
, TriggerRelidNameIndexId
,
19492 true, NULL
, 1, &skey
);
19493 while (HeapTupleIsValid(trigtup
= systable_getnext(scan
)))
19495 Form_pg_trigger pg_trigger
= (Form_pg_trigger
) GETSTRUCT(trigtup
);
19496 ObjectAddress trig
;
19498 /* Ignore triggers that weren't cloned */
19499 if (!OidIsValid(pg_trigger
->tgparentid
))
19503 * Ignore internal triggers that are implementation objects of foreign
19504 * keys, because these will be detached when the foreign keys
19507 if (OidIsValid(pg_trigger
->tgconstrrelid
))
19511 * This is ugly, but necessary: remove the dependency markings on the
19512 * trigger so that it can be removed.
19514 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
19516 DEPENDENCY_PARTITION_PRI
);
19517 deleteDependencyRecordsForClass(TriggerRelationId
, pg_trigger
->oid
,
19518 RelationRelationId
,
19519 DEPENDENCY_PARTITION_SEC
);
19521 /* remember this trigger to remove it below */
19522 ObjectAddressSet(trig
, TriggerRelationId
, pg_trigger
->oid
);
19523 add_exact_object_address(&trig
, objects
);
19526 /* make the dependency removal visible to the deletion below */
19527 CommandCounterIncrement();
19528 performMultipleDeletions(objects
, DROP_RESTRICT
, PERFORM_DELETION_INTERNAL
);
19531 free_object_addresses(objects
);
19532 systable_endscan(scan
);
19533 table_close(tgrel
, RowExclusiveLock
);
19537 * Before acquiring lock on an index, acquire the same lock on the owning
19540 struct AttachIndexCallbackState
19544 bool lockedParentTbl
;
19548 RangeVarCallbackForAttachIndex(const RangeVar
*rv
, Oid relOid
, Oid oldRelOid
,
19551 struct AttachIndexCallbackState
*state
;
19552 Form_pg_class classform
;
19555 state
= (struct AttachIndexCallbackState
*) arg
;
19557 if (!state
->lockedParentTbl
)
19559 LockRelationOid(state
->parentTblOid
, AccessShareLock
);
19560 state
->lockedParentTbl
= true;
19564 * If we previously locked some other heap, and the name we're looking up
19565 * no longer refers to an index on that relation, release the now-useless
19566 * lock. XXX maybe we should do *after* we verify whether the index does
19567 * not actually belong to the same relation ...
19569 if (relOid
!= oldRelOid
&& OidIsValid(state
->partitionOid
))
19571 UnlockRelationOid(state
->partitionOid
, AccessShareLock
);
19572 state
->partitionOid
= InvalidOid
;
19575 /* Didn't find a relation, so no need for locking or permission checks. */
19576 if (!OidIsValid(relOid
))
19579 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relOid
));
19580 if (!HeapTupleIsValid(tuple
))
19581 return; /* concurrently dropped, so nothing to do */
19582 classform
= (Form_pg_class
) GETSTRUCT(tuple
);
19583 if (classform
->relkind
!= RELKIND_PARTITIONED_INDEX
&&
19584 classform
->relkind
!= RELKIND_INDEX
)
19586 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19587 errmsg("\"%s\" is not an index", rv
->relname
)));
19588 ReleaseSysCache(tuple
);
19591 * Since we need only examine the heap's tupledesc, an access share lock
19592 * on it (preventing any DDL) is sufficient.
19594 state
->partitionOid
= IndexGetRelation(relOid
, false);
19595 LockRelationOid(state
->partitionOid
, AccessShareLock
);
19599 * ALTER INDEX i1 ATTACH PARTITION i2
19601 static ObjectAddress
19602 ATExecAttachPartitionIdx(List
**wqueue
, Relation parentIdx
, RangeVar
*name
)
19606 Relation parentTbl
;
19607 ObjectAddress address
;
19610 struct AttachIndexCallbackState state
;
19613 * We need to obtain lock on the index 'name' to modify it, but we also
19614 * need to read its owning table's tuple descriptor -- so we need to lock
19615 * both. To avoid deadlocks, obtain lock on the table before doing so on
19616 * the index. Furthermore, we need to examine the parent table of the
19617 * partition, so lock that one too.
19619 state
.partitionOid
= InvalidOid
;
19620 state
.parentTblOid
= parentIdx
->rd_index
->indrelid
;
19621 state
.lockedParentTbl
= false;
19623 RangeVarGetRelidExtended(name
, AccessExclusiveLock
, 0,
19624 RangeVarCallbackForAttachIndex
,
19627 if (!OidIsValid(partIdxId
))
19629 (errcode(ERRCODE_UNDEFINED_OBJECT
),
19630 errmsg("index \"%s\" does not exist", name
->relname
)));
19632 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19633 partIdx
= relation_open(partIdxId
, AccessExclusiveLock
);
19635 /* we already hold locks on both tables, so this is safe: */
19636 parentTbl
= relation_open(parentIdx
->rd_index
->indrelid
, AccessShareLock
);
19637 partTbl
= relation_open(partIdx
->rd_index
->indrelid
, NoLock
);
19639 ObjectAddressSet(address
, RelationRelationId
, RelationGetRelid(partIdx
));
19641 /* Silently do nothing if already in the right state */
19642 currParent
= partIdx
->rd_rel
->relispartition
?
19643 get_partition_parent(partIdxId
, false) : InvalidOid
;
19644 if (currParent
!= RelationGetRelid(parentIdx
))
19646 IndexInfo
*childInfo
;
19647 IndexInfo
*parentInfo
;
19651 PartitionDesc partDesc
;
19653 cldConstrId
= InvalidOid
;
19656 * If this partition already has an index attached, refuse the
19659 refuseDupeIndexAttach(parentIdx
, partIdx
, partTbl
);
19661 if (OidIsValid(currParent
))
19663 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19664 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19665 RelationGetRelationName(partIdx
),
19666 RelationGetRelationName(parentIdx
)),
19667 errdetail("Index \"%s\" is already attached to another index.",
19668 RelationGetRelationName(partIdx
))));
19670 /* Make sure it indexes a partition of the other index's table */
19671 partDesc
= RelationGetPartitionDesc(parentTbl
, true);
19673 for (i
= 0; i
< partDesc
->nparts
; i
++)
19675 if (partDesc
->oids
[i
] == state
.partitionOid
)
19683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19685 RelationGetRelationName(partIdx
),
19686 RelationGetRelationName(parentIdx
)),
19687 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19688 RelationGetRelationName(partIdx
),
19689 RelationGetRelationName(parentTbl
))));
19691 /* Ensure the indexes are compatible */
19692 childInfo
= BuildIndexInfo(partIdx
);
19693 parentInfo
= BuildIndexInfo(parentIdx
);
19694 attmap
= build_attrmap_by_name(RelationGetDescr(partTbl
),
19695 RelationGetDescr(parentTbl
),
19697 if (!CompareIndexInfo(childInfo
, parentInfo
,
19698 partIdx
->rd_indcollation
,
19699 parentIdx
->rd_indcollation
,
19700 partIdx
->rd_opfamily
,
19701 parentIdx
->rd_opfamily
,
19704 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19705 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19706 RelationGetRelationName(partIdx
),
19707 RelationGetRelationName(parentIdx
)),
19708 errdetail("The index definitions do not match.")));
19711 * If there is a constraint in the parent, make sure there is one in
19714 constraintOid
= get_relation_idx_constraint_oid(RelationGetRelid(parentTbl
),
19715 RelationGetRelid(parentIdx
));
19717 if (OidIsValid(constraintOid
))
19719 cldConstrId
= get_relation_idx_constraint_oid(RelationGetRelid(partTbl
),
19721 if (!OidIsValid(cldConstrId
))
19723 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
19724 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19725 RelationGetRelationName(partIdx
),
19726 RelationGetRelationName(parentIdx
)),
19727 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19728 RelationGetRelationName(parentIdx
),
19729 RelationGetRelationName(parentTbl
),
19730 RelationGetRelationName(partIdx
))));
19733 /* All good -- do it */
19734 IndexSetParentIndex(partIdx
, RelationGetRelid(parentIdx
));
19735 if (OidIsValid(constraintOid
))
19736 ConstraintSetParentConstraint(cldConstrId
, constraintOid
,
19737 RelationGetRelid(partTbl
));
19739 free_attrmap(attmap
);
19741 validatePartitionedIndex(parentIdx
, parentTbl
);
19744 relation_close(parentTbl
, AccessShareLock
);
19745 /* keep these locks till commit */
19746 relation_close(partTbl
, NoLock
);
19747 relation_close(partIdx
, NoLock
);
19753 * Verify whether the given partition already contains an index attached
19754 * to the given partitioned index. If so, raise an error.
19757 refuseDupeIndexAttach(Relation parentIdx
, Relation partIdx
, Relation partitionTbl
)
19761 existingIdx
= index_get_partition(partitionTbl
,
19762 RelationGetRelid(parentIdx
));
19763 if (OidIsValid(existingIdx
))
19765 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
19766 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19767 RelationGetRelationName(partIdx
),
19768 RelationGetRelationName(parentIdx
)),
19769 errdetail("Another index is already attached for partition \"%s\".",
19770 RelationGetRelationName(partitionTbl
))));
19774 * Verify whether the set of attached partition indexes to a parent index on
19775 * a partitioned table is complete. If it is, mark the parent index valid.
19777 * This should be called each time a partition index is attached.
19780 validatePartitionedIndex(Relation partedIdx
, Relation partedTbl
)
19782 Relation inheritsRel
;
19787 bool updated
= false;
19789 Assert(partedIdx
->rd_rel
->relkind
== RELKIND_PARTITIONED_INDEX
);
19792 * Scan pg_inherits for this parent index. Count each valid index we find
19793 * (verifying the pg_index entry for each), and if we reach the total
19794 * amount we expect, we can mark this parent index as valid.
19796 inheritsRel
= table_open(InheritsRelationId
, AccessShareLock
);
19797 ScanKeyInit(&key
, Anum_pg_inherits_inhparent
,
19798 BTEqualStrategyNumber
, F_OIDEQ
,
19799 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19800 scan
= systable_beginscan(inheritsRel
, InheritsParentIndexId
, true,
19802 while ((inhTup
= systable_getnext(scan
)) != NULL
)
19804 Form_pg_inherits inhForm
= (Form_pg_inherits
) GETSTRUCT(inhTup
);
19806 Form_pg_index indexForm
;
19808 indTup
= SearchSysCache1(INDEXRELID
,
19809 ObjectIdGetDatum(inhForm
->inhrelid
));
19810 if (!HeapTupleIsValid(indTup
))
19811 elog(ERROR
, "cache lookup failed for index %u", inhForm
->inhrelid
);
19812 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19813 if (indexForm
->indisvalid
)
19815 ReleaseSysCache(indTup
);
19818 /* Done with pg_inherits */
19819 systable_endscan(scan
);
19820 table_close(inheritsRel
, AccessShareLock
);
19823 * If we found as many inherited indexes as the partitioned table has
19824 * partitions, we're good; update pg_index to set indisvalid.
19826 if (tuples
== RelationGetPartitionDesc(partedTbl
, true)->nparts
)
19830 Form_pg_index indexForm
;
19832 idxRel
= table_open(IndexRelationId
, RowExclusiveLock
);
19833 indTup
= SearchSysCacheCopy1(INDEXRELID
,
19834 ObjectIdGetDatum(RelationGetRelid(partedIdx
)));
19835 if (!HeapTupleIsValid(indTup
))
19836 elog(ERROR
, "cache lookup failed for index %u",
19837 RelationGetRelid(partedIdx
));
19838 indexForm
= (Form_pg_index
) GETSTRUCT(indTup
);
19840 indexForm
->indisvalid
= true;
19843 CatalogTupleUpdate(idxRel
, &indTup
->t_self
, indTup
);
19845 table_close(idxRel
, RowExclusiveLock
);
19846 heap_freetuple(indTup
);
19850 * If this index is in turn a partition of a larger index, validating it
19851 * might cause the parent to become valid also. Try that.
19853 if (updated
&& partedIdx
->rd_rel
->relispartition
)
19857 Relation parentIdx
,
19860 /* make sure we see the validation we just did */
19861 CommandCounterIncrement();
19863 parentIdxId
= get_partition_parent(RelationGetRelid(partedIdx
), false);
19864 parentTblId
= get_partition_parent(RelationGetRelid(partedTbl
), false);
19865 parentIdx
= relation_open(parentIdxId
, AccessExclusiveLock
);
19866 parentTbl
= relation_open(parentTblId
, AccessExclusiveLock
);
19867 Assert(!parentIdx
->rd_index
->indisvalid
);
19869 validatePartitionedIndex(parentIdx
, parentTbl
);
19871 relation_close(parentIdx
, AccessExclusiveLock
);
19872 relation_close(parentTbl
, AccessExclusiveLock
);
19877 * Return an OID list of constraints that reference the given relation
19878 * that are marked as having a parent constraints.
19881 GetParentedForeignKeyRefs(Relation partition
)
19883 Relation pg_constraint
;
19886 ScanKeyData key
[2];
19887 List
*constraints
= NIL
;
19890 * If no indexes, or no columns are referenceable by FKs, we can avoid the
19893 if (RelationGetIndexList(partition
) == NIL
||
19894 bms_is_empty(RelationGetIndexAttrBitmap(partition
,
19895 INDEX_ATTR_BITMAP_KEY
)))
19898 /* Search for constraints referencing this table */
19899 pg_constraint
= table_open(ConstraintRelationId
, AccessShareLock
);
19900 ScanKeyInit(&key
[0],
19901 Anum_pg_constraint_confrelid
, BTEqualStrategyNumber
,
19902 F_OIDEQ
, ObjectIdGetDatum(RelationGetRelid(partition
)));
19903 ScanKeyInit(&key
[1],
19904 Anum_pg_constraint_contype
, BTEqualStrategyNumber
,
19905 F_CHAREQ
, CharGetDatum(CONSTRAINT_FOREIGN
));
19907 /* XXX This is a seqscan, as we don't have a usable index */
19908 scan
= systable_beginscan(pg_constraint
, InvalidOid
, true, NULL
, 2, key
);
19909 while ((tuple
= systable_getnext(scan
)) != NULL
)
19911 Form_pg_constraint constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
19914 * We only need to process constraints that are part of larger ones.
19916 if (!OidIsValid(constrForm
->conparentid
))
19919 constraints
= lappend_oid(constraints
, constrForm
->oid
);
19922 systable_endscan(scan
);
19923 table_close(pg_constraint
, AccessShareLock
);
19925 return constraints
;
19929 * During DETACH PARTITION, verify that any foreign keys pointing to the
19930 * partitioned table would not become invalid. An error is raised if any
19931 * referenced values exist.
19934 ATDetachCheckNoForeignKeyRefs(Relation partition
)
19939 constraints
= GetParentedForeignKeyRefs(partition
);
19941 foreach(cell
, constraints
)
19943 Oid constrOid
= lfirst_oid(cell
);
19945 Form_pg_constraint constrForm
;
19947 Trigger trig
= {0};
19949 tuple
= SearchSysCache1(CONSTROID
, ObjectIdGetDatum(constrOid
));
19950 if (!HeapTupleIsValid(tuple
))
19951 elog(ERROR
, "cache lookup failed for constraint %u", constrOid
);
19952 constrForm
= (Form_pg_constraint
) GETSTRUCT(tuple
);
19954 Assert(OidIsValid(constrForm
->conparentid
));
19955 Assert(constrForm
->confrelid
== RelationGetRelid(partition
));
19957 /* prevent data changes into the referencing table until commit */
19958 rel
= table_open(constrForm
->conrelid
, ShareLock
);
19960 trig
.tgoid
= InvalidOid
;
19961 trig
.tgname
= NameStr(constrForm
->conname
);
19962 trig
.tgenabled
= TRIGGER_FIRES_ON_ORIGIN
;
19963 trig
.tgisinternal
= true;
19964 trig
.tgconstrrelid
= RelationGetRelid(partition
);
19965 trig
.tgconstrindid
= constrForm
->conindid
;
19966 trig
.tgconstraint
= constrForm
->oid
;
19967 trig
.tgdeferrable
= false;
19968 trig
.tginitdeferred
= false;
19969 /* we needn't fill in remaining fields */
19971 RI_PartitionRemove_Check(&trig
, rel
, partition
);
19973 ReleaseSysCache(tuple
);
19975 table_close(rel
, NoLock
);
19980 * resolve column compression specification to compression method.
19983 GetAttributeCompression(Oid atttypid
, const char *compression
)
19987 if (compression
== NULL
|| strcmp(compression
, "default") == 0)
19988 return InvalidCompressionMethod
;
19991 * To specify a nondefault method, the column data type must be toastable.
19992 * Note this says nothing about whether the column's attstorage setting
19993 * permits compression; we intentionally allow attstorage and
19994 * attcompression to be independent. But with a non-toastable type,
19995 * attstorage could not be set to a value that would permit compression.
19997 * We don't actually need to enforce this, since nothing bad would happen
19998 * if attcompression were non-default; it would never be consulted. But
19999 * it seems more user-friendly to complain about a certainly-useless
20000 * attempt to set the property.
20002 if (!TypeIsToastable(atttypid
))
20004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20005 errmsg("column data type %s does not support compression",
20006 format_type_be(atttypid
))));
20008 cmethod
= CompressionNameToMethod(compression
);
20009 if (!CompressionMethodIsValid(cmethod
))
20011 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20012 errmsg("invalid compression method \"%s\"", compression
)));
20018 * resolve column storage specification
20021 GetAttributeStorage(Oid atttypid
, const char *storagemode
)
20025 if (pg_strcasecmp(storagemode
, "plain") == 0)
20026 cstorage
= TYPSTORAGE_PLAIN
;
20027 else if (pg_strcasecmp(storagemode
, "external") == 0)
20028 cstorage
= TYPSTORAGE_EXTERNAL
;
20029 else if (pg_strcasecmp(storagemode
, "extended") == 0)
20030 cstorage
= TYPSTORAGE_EXTENDED
;
20031 else if (pg_strcasecmp(storagemode
, "main") == 0)
20032 cstorage
= TYPSTORAGE_MAIN
;
20033 else if (pg_strcasecmp(storagemode
, "default") == 0)
20034 cstorage
= get_typstorage(atttypid
);
20037 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
20038 errmsg("invalid storage type \"%s\"",
20042 * safety check: do not allow toasted storage modes unless column datatype
20045 if (!(cstorage
== TYPSTORAGE_PLAIN
|| TypeIsToastable(atttypid
)))
20047 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
20048 errmsg("column data type %s can only have storage PLAIN",
20049 format_type_be(atttypid
))));
20055 * Struct with context of new partition for inserting rows from split partition
20057 typedef struct SplitPartitionContext
20059 ExprState
*partqualstate
; /* expression for checking slot for partition
20060 * (NULL for DEFAULT partition) */
20061 BulkInsertState bistate
; /* state of bulk inserts for partition */
20062 TupleTableSlot
*dstslot
; /* slot for inserting row into partition */
20063 Relation partRel
; /* relation for partition */
20064 } SplitPartitionContext
;
20068 * createSplitPartitionContext: create context for partition and fill it
20070 static SplitPartitionContext
*
20071 createSplitPartitionContext(Relation partRel
)
20073 SplitPartitionContext
*pc
;
20075 pc
= (SplitPartitionContext
*) palloc0(sizeof(SplitPartitionContext
));
20076 pc
->partRel
= partRel
;
20079 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20080 * don't bother using it.
20082 pc
->bistate
= GetBulkInsertState();
20084 /* Create tuple slot for new partition. */
20085 pc
->dstslot
= MakeSingleTupleTableSlot(RelationGetDescr(pc
->partRel
),
20086 table_slot_callbacks(pc
->partRel
));
20087 ExecStoreAllNullTuple(pc
->dstslot
);
20093 * deleteSplitPartitionContext: delete context for partition
20096 deleteSplitPartitionContext(SplitPartitionContext
*pc
, int ti_options
)
20098 ExecDropSingleTupleTableSlot(pc
->dstslot
);
20099 FreeBulkInsertState(pc
->bistate
);
20101 table_finish_bulk_insert(pc
->partRel
, ti_options
);
20107 * moveSplitTableRows: scan split partition (splitRel) of partitioned table
20108 * (rel) and move rows into new partitions.
20110 * New partitions description:
20111 * partlist: list of pointers to SinglePartitionSpec structures.
20112 * newPartRels: list of Relations.
20113 * defaultPartOid: oid of DEFAULT partition, for table rel.
20116 moveSplitTableRows(Relation rel
, Relation splitRel
, List
*partlist
, List
*newPartRels
, Oid defaultPartOid
)
20118 /* The FSM is empty, so don't bother using it. */
20119 int ti_options
= TABLE_INSERT_SKIP_FSM
;
20124 TupleTableSlot
*srcslot
;
20125 ExprContext
*econtext
;
20126 TableScanDesc scan
;
20128 MemoryContext oldCxt
;
20129 List
*partContexts
= NIL
;
20130 TupleConversionMap
*tuple_map
;
20131 SplitPartitionContext
*defaultPartCtx
= NULL
,
20133 bool isOldDefaultPart
= false;
20135 mycid
= GetCurrentCommandId(true);
20137 estate
= CreateExecutorState();
20139 forboth(listptr
, partlist
, listptr2
, newPartRels
)
20141 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
20143 pc
= createSplitPartitionContext((Relation
) lfirst(listptr2
));
20145 if (sps
->bound
->is_default
)
20147 /* We should not create constraint for detached DEFAULT partition. */
20148 defaultPartCtx
= pc
;
20152 List
*partConstraint
;
20154 /* Build expression execution states for partition check quals. */
20155 partConstraint
= get_qual_from_partbound(rel
, sps
->bound
);
20157 (List
*) eval_const_expressions(NULL
,
20158 (Node
*) partConstraint
);
20159 /* Make boolean expression for ExecCheck(). */
20160 partConstraint
= list_make1(make_ands_explicit(partConstraint
));
20163 * Map the vars in the constraint expression from rel's attnos to
20166 partConstraint
= map_partition_varattnos(partConstraint
,
20169 pc
->partqualstate
=
20170 ExecPrepareExpr((Expr
*) linitial(partConstraint
), estate
);
20171 Assert(pc
->partqualstate
!= NULL
);
20174 /* Store partition context into list. */
20175 partContexts
= lappend(partContexts
, pc
);
20179 * Create partition context for DEFAULT partition. We can insert values
20180 * into this partition in case spaces with values between new partitions.
20182 if (!defaultPartCtx
&& OidIsValid(defaultPartOid
))
20184 /* Indicate that we allocate context for old DEFAULT partition */
20185 isOldDefaultPart
= true;
20186 defaultPartCtx
= createSplitPartitionContext(table_open(defaultPartOid
, AccessExclusiveLock
));
20189 econtext
= GetPerTupleExprContext(estate
);
20191 /* Create necessary tuple slot. */
20192 srcslot
= MakeSingleTupleTableSlot(RelationGetDescr(splitRel
),
20193 table_slot_callbacks(splitRel
));
20196 * Map computing for moving attributes of split partition to new partition
20197 * (for first new partition, but other new partitions can use the same
20200 pc
= (SplitPartitionContext
*) lfirst(list_head(partContexts
));
20201 tuple_map
= convert_tuples_by_name(RelationGetDescr(splitRel
),
20202 RelationGetDescr(pc
->partRel
));
20204 /* Scan through the rows. */
20205 snapshot
= RegisterSnapshot(GetLatestSnapshot());
20206 scan
= table_beginscan(splitRel
, snapshot
, 0, NULL
);
20209 * Switch to per-tuple memory context and reset it for each tuple
20210 * produced, so we don't leak memory.
20212 oldCxt
= MemoryContextSwitchTo(GetPerTupleMemoryContext(estate
));
20214 while (table_scan_getnextslot(scan
, ForwardScanDirection
, srcslot
))
20216 bool found
= false;
20217 TupleTableSlot
*insertslot
;
20219 /* Extract data from old tuple. */
20220 slot_getallattrs(srcslot
);
20222 econtext
->ecxt_scantuple
= srcslot
;
20224 /* Search partition for current slot srcslot. */
20225 foreach(listptr
, partContexts
)
20227 pc
= (SplitPartitionContext
*) lfirst(listptr
);
20229 if (pc
->partqualstate
/* skip DEFAULT partition */ &&
20230 ExecCheck(pc
->partqualstate
, econtext
))
20235 ResetExprContext(econtext
);
20239 /* Use DEFAULT partition if it exists. */
20240 if (defaultPartCtx
)
20241 pc
= defaultPartCtx
;
20244 (errcode(ERRCODE_CHECK_VIOLATION
),
20245 errmsg("can not find partition for split partition row"),
20246 errtable(splitRel
)));
20251 /* Need to use map to copy attributes. */
20252 insertslot
= execute_attr_map_slot(tuple_map
->attrMap
, srcslot
, pc
->dstslot
);
20256 /* Copy attributes directly. */
20257 insertslot
= pc
->dstslot
;
20259 ExecClearTuple(insertslot
);
20261 memcpy(insertslot
->tts_values
, srcslot
->tts_values
,
20262 sizeof(Datum
) * srcslot
->tts_nvalid
);
20263 memcpy(insertslot
->tts_isnull
, srcslot
->tts_isnull
,
20264 sizeof(bool) * srcslot
->tts_nvalid
);
20266 ExecStoreVirtualTuple(insertslot
);
20269 /* Write the tuple out to the new relation. */
20270 table_tuple_insert(pc
->partRel
, insertslot
, mycid
,
20271 ti_options
, pc
->bistate
);
20273 ResetExprContext(econtext
);
20275 CHECK_FOR_INTERRUPTS();
20278 MemoryContextSwitchTo(oldCxt
);
20280 table_endscan(scan
);
20281 UnregisterSnapshot(snapshot
);
20284 free_conversion_map(tuple_map
);
20286 ExecDropSingleTupleTableSlot(srcslot
);
20288 FreeExecutorState(estate
);
20290 foreach(listptr
, partContexts
)
20291 deleteSplitPartitionContext((SplitPartitionContext
*) lfirst(listptr
), ti_options
);
20293 /* Need to close table and free buffers for DEFAULT partition. */
20294 if (isOldDefaultPart
)
20296 Relation defaultPartRel
= defaultPartCtx
->partRel
;
20298 deleteSplitPartitionContext(defaultPartCtx
, ti_options
);
20299 /* Keep the lock until commit. */
20300 table_close(defaultPartRel
, NoLock
);
20305 * createPartitionTable: create table for a new partition with given name
20306 * (newPartName) like table (modelRel)
20308 * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
20309 * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY EXCLUDING STATISTICS)
20311 * Also, this function sets the new partition access method same as parent
20312 * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
20313 * checks that parent and child tables have compatible persistence.
20315 * Function returns the created relation (locked in AccessExclusiveLock mode).
20318 createPartitionTable(RangeVar
*newPartName
, Relation modelRel
,
20319 AlterTableUtilityContext
*context
)
20321 CreateStmt
*createStmt
;
20322 TableLikeClause
*tlc
;
20323 PlannedStmt
*wrapper
;
20326 /* If existing rel is temp, it must belong to this session */
20327 if (modelRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
&&
20328 !modelRel
->rd_islocaltemp
)
20330 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
20331 errmsg("cannot create as partition of temporary relation of another session")));
20333 /* New partition should have the same persistence as modelRel */
20334 newPartName
->relpersistence
= modelRel
->rd_rel
->relpersistence
;
20336 createStmt
= makeNode(CreateStmt
);
20337 createStmt
->relation
= newPartName
;
20338 createStmt
->tableElts
= NIL
;
20339 createStmt
->inhRelations
= NIL
;
20340 createStmt
->constraints
= NIL
;
20341 createStmt
->options
= NIL
;
20342 createStmt
->oncommit
= ONCOMMIT_NOOP
;
20343 createStmt
->tablespacename
= get_tablespace_name(modelRel
->rd_rel
->reltablespace
);
20344 createStmt
->if_not_exists
= false;
20345 createStmt
->accessMethod
= get_am_name(modelRel
->rd_rel
->relam
);
20347 tlc
= makeNode(TableLikeClause
);
20348 tlc
->relation
= makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel
)),
20349 RelationGetRelationName(modelRel
), -1);
20352 * Indexes will be inherited on "attach new partitions" stage, after data
20353 * moving. We also don't copy the extended statistics for consistency
20354 * with CREATE TABLE PARTITION OF.
20356 tlc
->options
= CREATE_TABLE_LIKE_ALL
&
20357 ~(CREATE_TABLE_LIKE_INDEXES
| CREATE_TABLE_LIKE_IDENTITY
| CREATE_TABLE_LIKE_STATISTICS
);
20358 tlc
->relationOid
= InvalidOid
;
20359 createStmt
->tableElts
= lappend(createStmt
->tableElts
, tlc
);
20361 /* Need to make a wrapper PlannedStmt. */
20362 wrapper
= makeNode(PlannedStmt
);
20363 wrapper
->commandType
= CMD_UTILITY
;
20364 wrapper
->canSetTag
= false;
20365 wrapper
->utilityStmt
= (Node
*) createStmt
;
20366 wrapper
->stmt_location
= context
->pstmt
->stmt_location
;
20367 wrapper
->stmt_len
= context
->pstmt
->stmt_len
;
20369 ProcessUtility(wrapper
,
20370 context
->queryString
,
20372 PROCESS_UTILITY_SUBCOMMAND
,
20379 * Open the new partition with no lock, because we already have
20380 * AccessExclusiveLock placed there after creation.
20382 newRel
= table_openrv(newPartName
, NoLock
);
20385 * We intended to create the partition with the same persistence as the
20386 * parent table, but we still need to recheck because that might be
20387 * affected by the search_path. If the parent is permanent, so must be
20388 * all of its partitions.
20390 if (modelRel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
20391 newRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
20393 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
20394 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
20395 RelationGetRelationName(modelRel
))));
20397 /* Permanent rels cannot be partitions belonging to temporary parent */
20398 if (newRel
->rd_rel
->relpersistence
!= RELPERSISTENCE_TEMP
&&
20399 modelRel
->rd_rel
->relpersistence
== RELPERSISTENCE_TEMP
)
20401 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
20402 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
20403 RelationGetRelationName(modelRel
))));
20409 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
20412 ATExecSplitPartition(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
20413 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
)
20417 char relname
[NAMEDATALEN
];
20421 bool isSameName
= false;
20422 char tmpRelName
[NAMEDATALEN
];
20423 List
*newPartRels
= NIL
;
20424 ObjectAddress object
;
20425 Oid defaultPartOid
;
20427 defaultPartOid
= get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
20430 * We are going to detach and remove this partition: need to use exclusive
20431 * lock for preventing DML-queries to the partition.
20433 splitRel
= table_openrv(cmd
->name
, AccessExclusiveLock
);
20435 splitRelOid
= RelationGetRelid(splitRel
);
20437 /* Check descriptions of new partitions. */
20438 foreach(listptr
, cmd
->partlist
)
20440 Oid existing_relid
;
20441 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
20443 strlcpy(relname
, sps
->name
->relname
, NAMEDATALEN
);
20446 * Look up the namespace in which we are supposed to create the
20447 * partition, check we have permission to create there, lock it
20448 * against concurrent drop, and mark stmt->relation as
20449 * RELPERSISTENCE_TEMP if a temporary namespace is selected.
20451 sps
->name
->relpersistence
= rel
->rd_rel
->relpersistence
;
20453 RangeVarGetAndCheckCreationNamespace(sps
->name
, NoLock
, NULL
);
20456 * This would fail later on anyway if the relation already exists. But
20457 * by catching it here we can emit a nicer error message.
20459 existing_relid
= get_relname_relid(relname
, namespaceId
);
20460 if (existing_relid
== splitRelOid
&& !isSameName
)
20461 /* One new partition can have the same name as split partition. */
20463 else if (existing_relid
!= InvalidOid
)
20465 (errcode(ERRCODE_DUPLICATE_TABLE
),
20466 errmsg("relation \"%s\" already exists", relname
)));
20469 /* Detach split partition. */
20470 RemoveInheritance(splitRel
, rel
, false);
20471 /* Do the final part of detaching. */
20472 DetachPartitionFinalize(rel
, splitRel
, false, defaultPartOid
);
20475 * If new partition has the same name as split partition then we should
20476 * rename split partition for reusing name.
20481 * We must bump the command counter to make the split partition tuple
20482 * visible for renaming.
20484 CommandCounterIncrement();
20485 /* Rename partition. */
20486 sprintf(tmpRelName
, "split-%u-%X-tmp", RelationGetRelid(rel
), MyProcPid
);
20487 RenameRelationInternal(splitRelOid
, tmpRelName
, false, false);
20490 * We must bump the command counter to make the split partition tuple
20491 * visible after renaming.
20493 CommandCounterIncrement();
20496 /* Create new partitions (like split partition), without indexes. */
20497 foreach(listptr
, cmd
->partlist
)
20499 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
20500 Relation newPartRel
;
20502 newPartRel
= createPartitionTable(sps
->name
, rel
, context
);
20503 newPartRels
= lappend(newPartRels
, newPartRel
);
20506 /* Copy data from split partition to new partitions. */
20507 moveSplitTableRows(rel
, splitRel
, cmd
->partlist
, newPartRels
, defaultPartOid
);
20508 /* Keep the lock until commit. */
20509 table_close(splitRel
, NoLock
);
20511 /* Attach new partitions to partitioned table. */
20512 forboth(listptr
, cmd
->partlist
, listptr2
, newPartRels
)
20514 SinglePartitionSpec
*sps
= (SinglePartitionSpec
*) lfirst(listptr
);
20515 Relation newPartRel
= (Relation
) lfirst(listptr2
);
20518 * wqueue = NULL: verification for each cloned constraint is not
20521 attachPartitionTable(NULL
, rel
, newPartRel
, sps
->bound
);
20522 /* Keep the lock until commit. */
20523 table_close(newPartRel
, NoLock
);
20526 /* Drop split partition. */
20527 object
.classId
= RelationRelationId
;
20528 object
.objectId
= splitRelOid
;
20529 object
.objectSubId
= 0;
20530 /* Probably DROP_CASCADE is not needed. */
20531 performDeletion(&object
, DROP_RESTRICT
, 0);
20535 * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
20536 * of the partitioned table (rel) and move rows into the new partition
20540 moveMergedTablesRows(Relation rel
, List
*mergingPartitionsList
,
20541 Relation newPartRel
)
20545 /* The FSM is empty, so don't bother using it. */
20546 int ti_options
= TABLE_INSERT_SKIP_FSM
;
20548 BulkInsertState bistate
; /* state of bulk inserts for partition */
20549 TupleTableSlot
*dstslot
;
20551 mycid
= GetCurrentCommandId(true);
20553 /* Prepare a BulkInsertState for table_tuple_insert. */
20554 bistate
= GetBulkInsertState();
20556 /* Create necessary tuple slot. */
20557 dstslot
= MakeSingleTupleTableSlot(RelationGetDescr(newPartRel
),
20558 table_slot_callbacks(newPartRel
));
20559 ExecStoreAllNullTuple(dstslot
);
20561 foreach(listptr
, mergingPartitionsList
)
20563 Relation mergingPartition
= (Relation
) lfirst(listptr
);
20564 TupleTableSlot
*srcslot
;
20565 TupleConversionMap
*tuple_map
;
20566 TableScanDesc scan
;
20569 /* Create tuple slot for new partition. */
20570 srcslot
= MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition
),
20571 table_slot_callbacks(mergingPartition
));
20574 * Map computing for moving attributes of merged partition to new
20577 tuple_map
= convert_tuples_by_name(RelationGetDescr(mergingPartition
),
20578 RelationGetDescr(newPartRel
));
20580 /* Scan through the rows. */
20581 snapshot
= RegisterSnapshot(GetLatestSnapshot());
20582 scan
= table_beginscan(mergingPartition
, snapshot
, 0, NULL
);
20584 while (table_scan_getnextslot(scan
, ForwardScanDirection
, srcslot
))
20586 TupleTableSlot
*insertslot
;
20588 /* Extract data from old tuple. */
20589 slot_getallattrs(srcslot
);
20593 /* Need to use map to copy attributes. */
20594 insertslot
= execute_attr_map_slot(tuple_map
->attrMap
, srcslot
, dstslot
);
20598 /* Copy attributes directly. */
20599 insertslot
= dstslot
;
20601 ExecClearTuple(insertslot
);
20603 memcpy(insertslot
->tts_values
, srcslot
->tts_values
,
20604 sizeof(Datum
) * srcslot
->tts_nvalid
);
20605 memcpy(insertslot
->tts_isnull
, srcslot
->tts_isnull
,
20606 sizeof(bool) * srcslot
->tts_nvalid
);
20608 ExecStoreVirtualTuple(insertslot
);
20611 /* Write the tuple out to the new relation. */
20612 table_tuple_insert(newPartRel
, insertslot
, mycid
,
20613 ti_options
, bistate
);
20615 CHECK_FOR_INTERRUPTS();
20618 table_endscan(scan
);
20619 UnregisterSnapshot(snapshot
);
20622 free_conversion_map(tuple_map
);
20624 ExecDropSingleTupleTableSlot(srcslot
);
20627 ExecDropSingleTupleTableSlot(dstslot
);
20628 FreeBulkInsertState(bistate
);
20630 table_finish_bulk_insert(newPartRel
, ti_options
);
20634 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
20637 ATExecMergePartitions(List
**wqueue
, AlteredTableInfo
*tab
, Relation rel
,
20638 PartitionCmd
*cmd
, AlterTableUtilityContext
*context
)
20640 Relation newPartRel
;
20642 List
*mergingPartitionsList
= NIL
;
20643 Oid defaultPartOid
;
20648 * Lock all merged partitions, check them and create list with partitions
20651 foreach(listptr
, cmd
->partlist
)
20653 RangeVar
*name
= (RangeVar
*) lfirst(listptr
);
20654 Relation mergingPartition
;
20657 * We are going to detach and remove this partition: need to use
20658 * exclusive lock for preventing DML-queries to the partition.
20660 mergingPartition
= table_openrv(name
, AccessExclusiveLock
);
20662 /* Store a next merging partition into the list. */
20663 mergingPartitionsList
= lappend(mergingPartitionsList
,
20668 * Look up the namespace in which we are supposed to create the partition,
20669 * check we have permission to create there, lock it against concurrent
20670 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
20671 * namespace is selected.
20673 cmd
->name
->relpersistence
= rel
->rd_rel
->relpersistence
;
20675 RangeVarGetAndCheckCreationNamespace(cmd
->name
, NoLock
, NULL
);
20678 * Check if this name is already taken. This helps us to detect the
20679 * situation when one of the merging partitions has the same name as the
20680 * new partition. Otherwise, this would fail later on anyway but catching
20681 * this here allows us to emit a nicer error message.
20683 existingRelid
= get_relname_relid(cmd
->name
->relname
, namespaceId
);
20685 if (OidIsValid(existingRelid
))
20687 Relation sameNamePartition
= NULL
;
20689 foreach_ptr(RelationData
, mergingPartition
, mergingPartitionsList
)
20691 if (RelationGetRelid(mergingPartition
) == existingRelid
)
20693 sameNamePartition
= mergingPartition
;
20698 if (sameNamePartition
)
20701 * The new partition has the same name as one of merging
20704 char tmpRelName
[NAMEDATALEN
];
20706 /* Generate temporary name. */
20707 sprintf(tmpRelName
, "merge-%u-%X-tmp", RelationGetRelid(rel
), MyProcPid
);
20710 * Rename the existing partition with a temporary name, leaving it
20711 * free for the new partition. We don't need to care about this
20712 * in the future because we're going to eventually drop the
20713 * existing partition anyway.
20715 RenameRelationInternal(RelationGetRelid(sameNamePartition
),
20716 tmpRelName
, false, false);
20719 * We must bump the command counter to make the new partition
20720 * tuple visible for rename.
20722 CommandCounterIncrement();
20727 (errcode(ERRCODE_DUPLICATE_TABLE
),
20728 errmsg("relation \"%s\" already exists", cmd
->name
->relname
)));
20732 /* Detach all merged partitions. */
20734 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel
, true));
20735 foreach(listptr
, mergingPartitionsList
)
20737 Relation mergingPartition
= (Relation
) lfirst(listptr
);
20739 /* Remove the pg_inherits row first. */
20740 RemoveInheritance(mergingPartition
, rel
, false);
20741 /* Do the final part of detaching. */
20742 DetachPartitionFinalize(rel
, mergingPartition
, false, defaultPartOid
);
20745 /* Create table for new partition, use partitioned table as model. */
20746 newPartRel
= createPartitionTable(cmd
->name
, rel
, context
);
20748 /* Copy data from merged partitions to new partition. */
20749 moveMergedTablesRows(rel
, mergingPartitionsList
, newPartRel
);
20751 /* Drop the current partitions before attaching the new one. */
20752 foreach(listptr
, mergingPartitionsList
)
20754 ObjectAddress object
;
20755 Relation mergingPartition
= (Relation
) lfirst(listptr
);
20757 /* Get relation id before table_close() call. */
20758 object
.objectId
= RelationGetRelid(mergingPartition
);
20759 object
.classId
= RelationRelationId
;
20760 object
.objectSubId
= 0;
20762 /* Keep the lock until commit. */
20763 table_close(mergingPartition
, NoLock
);
20765 performDeletion(&object
, DROP_RESTRICT
, 0);
20767 list_free(mergingPartitionsList
);
20770 * Attach a new partition to the partitioned table. wqueue = NULL:
20771 * verification for each cloned constraint is not needed.
20773 attachPartitionTable(NULL
, rel
, newPartRel
, cmd
->bound
);
20775 /* Keep the lock until commit. */
20776 table_close(newPartRel
, NoLock
);